How async await works in JavaScript with setTimeout() - javascript

Why this code does not work
I want to learn the synchronization in the easy way
const post = [];
function getPost(post) {
console.log(post)
}
async function createPost (post) {
await setTimeout(() => {
post.push({
name: "John", work: "Developer"
})
}, 1000)
getPost(post);
}
createPost(post);

It doesn't work because setTimeout doesn't return a promise. If you await a non-Promise, you just get the value (almost¹) immediately. You'd also want to consume the promise returned by createPost, which your code isn't currently doing.
You'd have to make a wrapper for setTimeout that returns a promise, for instance as shown in my answer here, although these days I'd modify it a bit:
function timeout(delay, ...args) {
return new Promise(function(resolve) {
setTimeout(resolve, delay, ...args);
});
}
Here's that applied to your code, although I assume the setTimeout in your code is really a stand-in for some other async operation (ajax, MongoDB, etc.), and I wouldn't structure the code this way (I'd have post created and returned by createPost rather than being an array that createPost closes over):
function timeout(delay, ...args) {
return new Promise(function(resolve) {
setTimeout(resolve, delay, args);
});
}
const post = [];
function getPost(post) {
console.log(post)
}
async function createPost(post) {
await timeout(1000);
post.push({
name: "John", work: "Developer"
});
getPost(post);
}
// Going async at top level
(async () => {
await createPost(post);
})().catch(error => {
console.error(error);
});
¹ "almost immediately" a = await b; ... is syntactic sugar (really, really good sugar) for Promise.resolve(b).then(a => ...) (plus error handling). If b isn't a promise, the assignment to a happens in the microtask processing following the task the await occurs in. More about tasks and microtasks in my answer here.

Related

Async await issue on nodejs

I'm learning to use async/await and this is quite confused to me. As I know, async functions always return a promise. If the return value of an async function is not explicitly a promise, it will be implicitly wrapped in a promise.
async function foo() {
setTimeout(() => {
throw new Error('error');
}, 5000);
}
async function test() {
try {
await foo();
} catch (err) {
console.log(err);
}
console.log('not reached');
}
test();
Expected output: log the error after 5 secs, then log "not reach".
What actually happened: "not reach" gets logged right after I start the script, then the error gets logged after 5 seconds.
Attempts: the following code does exactly what I want, but I don't know what's the difference between those code.
Environment: nodejs 16.14.2
function foo() {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error('foo'));
}, 5000);
});
}
async function test() {
try {
await foo();
} catch (err) {
console.log(err);
}
console.log('not reached');
}
test();
Thanks a lot!
foo needs to actually resolve
Your example doesn't work because foo doesn't actually care about the timeout. It just starts the timeout then returns immediately. What you need to do is make foo resolve when the timeout fires, which actually requires you to construct a Promise explicitly.
async function foo() {
return new Promise(resolve => {
setTimeout(resolve, 5000)
})
}
Since you return a Promise, you could technically not declare foo as async, but it doesn't matter, and (I think) it's still helpful for other developers to see that the function returns a Promise without having to look through the implementation.
In your second example, foo returns a promise, setTimeout needs to produce a promise for it to work in your example.
For example
async function foo() {
// promise returned
return new Promise(resolve => {
setTimeout(() => { resolve() } , 5000)
})
}

If I use async/await, it doesn't run in order

I have a question about the async/await execution.
example codes
async firstMethod(){
new Promise((resolve, reject)) => {
setTimeout(() => {
resolve("test1");
}, 3000);
});
}
async secondMethod() {
new Promise((resolve, reject)) => {
setTimeout(() => {
resolve("test2");
}, 1000);
});
}
await firstMethod();
await secondMethod();
So, When the two methods are executed, the following results are obtained.
test2
test1
However, if a return is attached, the result value is as follows.
async firstMethod(){
return new Promise((resolve, reject)) => {
setTimeout(() => {
resolve("test1");
}, 3000);
});
}
async secondMethod() {
return new Promise((resolve, reject)) => {
setTimeout(() => {
resolve("test2");
}, 1000);
});
}
await firstMethod();
await secondMethod();
test1
test2
Why is that? I'd appreciate it if you could explain it.
In the first case you just await the outer promises (that are the firstMethod and secondMethod functions) and the inner promises created by new Promise do not get awaited, and simply run to completion in the background.
In the second case, because of promise unwrapping you await the returned inner promises together with the outer function promises.
The reason why in the first example those 2 functions do not run in order is because you are not waiting for anything. If you try to remove the async from the function definition in the first example you will get the same result because the function does not need the async because it does not require an await. What you could do to have the same result has the second example is by adding a await before the new Promise.
Note: You also do not need the async in the second example because you are not waiting for anything, you are just returning a Promise in which you wait outside the scope.

I want to print functions return values by using Async Awaits functionality in javascript promises [duplicate]

I am trying to use the new async features and I hope solving my problem will help others in the future. This is my code which is working:
async function asyncGenerator() {
// other code
while (goOn) {
// other code
var fileList = await listFiles(nextPageToken);
var parents = await requestParents(fileList);
// other code
}
// other code
}
function listFiles(token) {
return gapi.client.drive.files.list({
'maxResults': sizeResults,
'pageToken': token,
'q': query
});
}
The problem is, that my while loop runs too fast and the script sends too many requests per second to the google API. Therefore I would like to build a sleep function which delays the request. Thus I could also use this function to delay other requests. If there is another way to delay the request, please let me know.
Anyway, this is my new code which does not work. The response of the request is returned to the anonymous async function within the setTimeout, but I just do not know how I can return the response to the sleep function resp. to the initial asyncGenerator function.
async function asyncGenerator() {
// other code
while (goOn) {
// other code
var fileList = await sleep(listFiles, nextPageToken);
var parents = await requestParents(fileList);
// other code
}
// other code
}
function listFiles(token) {
return gapi.client.drive.files.list({
'maxResults': sizeResults,
'pageToken': token,
'q': query
});
}
async function sleep(fn, par) {
return await setTimeout(async function() {
await fn(par);
}, 3000, fn, par);
}
I have already tried some options: storing the response in a global variable and return it from the sleep function, callback within the anonymous function, etc.
Your sleep function does not work because setTimeout does not (yet?) return a promise that could be awaited. You will need to promisify it manually:
function timeout(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function sleep(fn, ...args) {
await timeout(3000);
return fn(...args);
}
Btw, to slow down your loop you probably don't want to use a sleep function that takes a callback and defers it like this. I recommend:
while (goOn) {
// other code
var [parents] = await Promise.all([
listFiles(nextPageToken).then(requestParents),
timeout(5000)
]);
// other code
}
which lets the computation of parents take at least 5 seconds.
The quick one-liner, inline way
await new Promise(resolve => setTimeout(resolve, 1000));
Since Node 7.6, you can combine the functions promisify function from the utils module with setTimeout() .
Node.js
const sleep = require('util').promisify(setTimeout)
Javascript
const sleep = m => new Promise(r => setTimeout(r, m))
Usage
(async () => {
console.time("Slept for")
await sleep(3000)
console.timeEnd("Slept for")
})()
setTimeout is not an async function, so you can't use it with ES7 async-await. But you could implement your sleep function using ES6 Promise:
function sleep (fn, par) {
return new Promise((resolve) => {
// wait 3s before calling fn(par)
setTimeout(() => resolve(fn(par)), 3000)
})
}
Then you'll be able to use this new sleep function with ES7 async-await:
var fileList = await sleep(listFiles, nextPageToken)
Please, note that I'm only answering your question about combining ES7 async/await with setTimeout, though it may not help solve your problem with sending too many requests per second.
Update: Modern node.js versions has a buid-in async timeout implementation, accessible via util.promisify helper:
const {promisify} = require('util');
const setTimeoutAsync = promisify(setTimeout);
Timers Promises API
await setTimeout finally arrived with Node.js 16, removing the need to use util.promisify():
import { setTimeout } from 'timers/promises';
(async () => {
const result = await setTimeout(2000, 'resolved')
// Executed after 2 seconds
console.log(result); // "resolved"
})()
Official Node.js docs: Timers Promises API (library already built in Node)
If you would like to use the same kind of syntax as setTimeout you can write a helper function like this:
const setAsyncTimeout = (cb, timeout = 0) => new Promise(resolve => {
setTimeout(() => {
cb();
resolve();
}, timeout);
});
You can then call it like so:
const doStuffAsync = async () => {
await setAsyncTimeout(() => {
// Do stuff
}, 1000);
await setAsyncTimeout(() => {
// Do more stuff
}, 500);
await setAsyncTimeout(() => {
// Do even more stuff
}, 2000);
};
doStuffAsync();
I made a gist: https://gist.github.com/DaveBitter/f44889a2a52ad16b6a5129c39444bb57
I leave this code snippet here for someone who wants to fetch API call (e.g. get clients) with setTimeout:
const { data } = await new Promise(resolve => setTimeout(resolve, 250)).then(() => getClientsService())
setName(data.name || '')
setEmail(data.email || '')
await new Promise(resolve => setTimeout(() => { resolve({ data: 'your return data'}) }, 1000))
var testAwait = function () {
var promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Inside test await');
}, 1000);
});
return promise;
}
var asyncFunction = async function() {
await testAwait().then((data) => {
console.log(data);
})
return 'hello asyncFunction';
}
asyncFunction().then((data) => {
console.log(data);
});
//Inside test await
//hello asyncFunction
This is my version with nodejs now in 2020 in AWS labdas
const sleep = require('util').promisify(setTimeout)
async function f1 (some){
...
}
async function f2 (thing){
...
}
module.exports.someFunction = async event => {
...
await f1(some)
await sleep(5000)
await f2(thing)
...
}
await setTimeout(()=>{}, 200);
Will work if your Node version is 15 and above.
Made a util inspired from Dave's answer
Basically passed in a done callback to call when the operation is finished.
// Function to timeout if a request is taking too long
const setAsyncTimeout = (cb, timeout = 0) => new Promise((resolve, reject) => {
cb(resolve);
setTimeout(() => reject('Request is taking too long to response'), timeout);
});
This is how I use it:
try {
await setAsyncTimeout(async done => {
const requestOne = await someService.post(configs);
const requestTwo = await someService.get(configs);
const requestThree = await someService.post(configs);
done();
}, 5000); // 5 seconds max for this set of operations
}
catch (err) {
console.error('[Timeout] Unable to complete the operation.', err);
}
The following code works in Chrome and Firefox and maybe other browsers.
function timeout(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function sleep(fn, ...args) {
await timeout(3000);
return fn(...args);
}
But in Internet Explorer I get a Syntax Error for the "(resolve **=>** setTimeout..."
How to Log all the responses at once?
async function sayHello(name) {
let greet = `Hey! ${name} very nice to meet you bud.`;
setTimeout(() => {
return {
greet,
createdAt: new Date(),
};
}, 1000);
}
const response1 = async () => await sayHello("sounish");
const response2 = async () => await sayHello("alex");
const response3 = async () => await sayHello("bill");
async function getData() {
const data1 = await sayHello("sounish");
const data2 = await sayHello("alex");
const data3 = await sayHello("bill");
return { data1, data2, data3 };
}
Promise.all([sayHello("sounish"), sayHello("alex"), sayHello("bill")]).then(
(allResponses) => {
console.log({ allResponses });
}
);
getData().then((allData) => {
console.log({ allData });
});
I would like to point out a robust extension to Promise.all. A rather elegant solution that works with one promise to be time-limited only is to race the promise with a timeout (such as new Promise((resolve) => setTimeout(resolve, timeout))).
await new Promise.race([myPromise, timeoutPromise])
will continue as soon as one of the promises finished. myPromise then can internally await a different timeout, or simply make use of Promise.all
const timeout = ms => new Promise((resolve) => setTimeout(resolve, ms));
await Promise.race([
Promise.all([myPromise, timeout(500)]),
timeout(5000)
]);
The result is an asynchronous call that does not run more often than twice a second, with a timeout of 5 seconds in case of some (network/server?) error.
Moreover, you can make this very versatile and customizable function as such:
function callWithTimeout(promise, msTimeout=5000, throws=false) {
const timeout = ms => new Promise((resolve, reject) =>
setTimeout(throws ? reject : resolve, ms));
await Promise.race([
//depends whether you want to wait there or just pass the promise itself
Promise.all([promise, timeout(500)]),
timeout(msTimeout)
]);
}
Which ultimately lets you customize the timeout time and the whether the promise should succeed or throw on timeout. Having such robust general implementation can save you a lot of pain in the future. You can also set a string instead of boolean as throws and bind this variable to the reject for custom error message: reject.bind(undefined, throws)
Note that you should not pass your promise with await:
const myPromise = async x => x;
//will never time out and not because myPromise will finish immediatelly
callWithTimeout(await myPromise(), 200, true);
//will possibly timeout after 200 ms with an exception
callWithTimeout(myPromise(), 200, true);
With the marked answer I had a lint error [no-promise-executor-return] so I found here the corrected version, using curly brackets in order to make explicit the intention of not returning anything:
const timeout = (ms) =>
new Promise(resolve => {
setTimeout(resolve, ms)
})
This is a quicker fix in one-liner.
Hope this will help.
// WAIT FOR 200 MILISECONDS TO GET DATA //
await setTimeout(()=>{}, 200);

Unsure about usage of async and callbacks in javascript

Description:
I have a Mocha Test within my Node-App that should test whether a DB-Export of Mongo-DB-JSON-Documents is done correctly.
In my test I besides other tests also test if the download-directory is not empty.
Expected result:
The test should await the downloads and only then check whether the directory is empty.
Actual Result:
The test returns always green.
My Question:
I understood that we have callbacks but promises are better.
I understood that async await is some syntactic sugar to promises.
And I understood that there is even RxJS (which I do not use here)
Somehow I have to deal with the callback from mogodb-backup.
See https://www.npmjs.com/package/mongodb-backup
I do not understand what I am doing wrong so that the tests always turn green (running in parallel to the download)
mocha-test.js
describe('Database.downloadDocumentsOfType_KEYS()', function () {
it('should result in data/exportFromCosmos/KEYS/admin/ag-data/ not being empty', function () {
const config = {
documents: ['DEFAULT', 'KEYS'],
exportpathDEFAULT: 'data/exportFromCosmos/DEFAULT/',
exportpathKEYS: 'data/exportFromCosmos/KEYS/',
uploadpath: 'data/uploadToAzureBlob/',
crosscheckFile: 'data/crosscheckFile.txt'
}
async function f() {
await Database.downloadDocumentsOfType_KEYS().then(expect(dir(config.exportpathKEYS + 'admin/ag-data/')).to.not.be.empty)
}
f()
})
})
Databasemodule-to-be-tested.js
const mongodbbackup = require('mongodb-backup')
const Database = {
downloadDocumentsOfType_DEFAULT: () => {
new Promise((resolve) => mongodbbackup({
uri: process.env.DB_CONNECTION_STRING_READ,
root: 'data/exportFromCosmos/DEFAULT',
parser: 'json',
callback: function(err) {
if (err) {
reject()
} else {
resolve()
}
}
)}
}
async function f() {
await Database.downloadDocumentsOfType_KEYS().then(e)
}
f()
This fires off the asynchronous function immediately and
it('...', function (){})
finishes immediately.
So you need to use
describe('...',async function(){
it('...',async function(){
const f = async function(){
await Database.downloadDocumentsOfType_KEYS();
expect(dir(config.exportpathKEYS + 'admin/ag-data/')).to.not.be.empty);
};
await f();
});
});
Also,
new Promise((resolve) => mongodbbackup({...
should be
new Promise((resolve,reject) => mongodbbackup({
Otherwise reject is undefined

JavaScript Async/Await

I'm trying to understand JavaScript async/await. How can I rewrite the below such that the output is "Hi" then "Bye" instead of "Bye" then "Hi":
JSFiddle
sayHi()
.then(sayBye);
async function sayHi() {
await setTimeout(function() {
$("#myOutput").text('hi');
}, 1000);
}
async function sayBye() {
$("#myOutput").text('bye');
}
In order to await setTimeout it needs to be wrapped into Promise. Then with async/await you can flatten your code write it without Promise then API:
(async () => { // await has to be inside async function, anonymous in this case
await sayHi()
sayBye()
})()
async function sayHi() {
return new Promise(function (resolve) {
$("#myOutput").text('hi');
setTimeout(function() {
resolve()
}, 1000)
});
}
async function sayBye() {
$("#myOutput").text('bye');
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="myOutput"></div>
setTimeout doesn't return a Promise. Create a helper function to wrap it in a Promise and then you can await it.
function delay(fn, t) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(fn());
}, t);
});
}
sayHi()
.then(sayBye);
async function sayHi() {
await delay(() => {
//$("#myOutput").text('hi');
console.log("Hi");
}, 1000);
}
async function sayBye() {
//$("#myOutput").text('bye');
console.log("Bye");
}
Use the Promise way
sayHi()
.then(sayBye);
function sayHi() {
return new Promise(resolve => {
setTimeout(()=> {
$("#myOutput").text('hi'), resolve()
}, 1000);
})
}
async function sayBye() {
$("#myOutput").text('bye');
}
or the sayHi like this:
async function sayHi() {
await new Promise(resolve => {
setTimeout(()=> {
$("#myOutput").text('hi'), resolve()
}, 1000)
})
}
Using async/await is an excellent way to build acynchronous code in a quite controllable way. Promise based async function goes into microtasks depository, which event loop executes before events and methods contained in ordinary DOM refresh/web API depository (setTimeout() for example). However some versions of Opera and Firefox browsers set priority to setTimeout() over microtasks depository. Anyway, you can control order of execution if you combine Promise based function with async/await enabled function. For example:
// create function that returns Promise
let hi = () => {
return new Promise((resolve, reject) => {
setTimeout(_ => {
resolve('Hi '); // after 1500ms function is resolved and returns 'Hi '
}, 1500);
});
}
// create ordinary function that will return 'bye'
let sayBye = () => {
return 'Bye';
}
// create third function that will be async 'enabled',
// so it can use await keyword before Promise based functions
let sayHi = async () => {
let first = await hi(); // we store 'Hi ' into 'first' variable
console.log(first);
let second = sayBye(); // this execution will await until hi() is finished
console.log(second);
}
// execute async enabled function
sayHi();
We can add try / catch block inside sayHi() function for controlling error on promise reject(), but this is out of scope of your question.
Have a nice day!
You cannot use async/await for functions that are not returning Promise
When an async function is called, it returns a Promise. When the async function returns a value, the Promise will be resolved with the returned value. When the async function throws an exception or some value, the Promise will be rejected with the thrown value.
An async function can contain an await expression, that pauses the execution of the async function and waits for the passed Promise's resolution, and then resumes the async function's execution and returns the resolved value.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
Usually it is used to handle data that comes from server, because when you have multiple queries it can override previous one and you will handle the wrong one.
Async/await lets you handle exactly data you are awaiting for.

Categories