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 await
ed. 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));
resolve
appears twice. If it's still confusing, take a look at the MDN docs for Promise.
await new Promise((resolve, reject) => setTimeout(resolve, 1000));
. So, resolve
and reject
are callbacks exposed when you create a Promise. You're simply telling setTimeout
to execute resolve()
.
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")
})()
await require('util').promisify(setTimeout)(3000)
can also be achieved without require by: await setTimeout[Object.getOwnPropertySymbols(setTimeout)[0]](3000)
getOwnPropertySymbols
version... if it ain't broke...!
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);
fn
throws the errror would not be caught.
new Promise
where you can sleep.catch
it.
setTimeout
callback and the new Promise
callback has been done for long. It will bubble to the global context and be thrown as an unhandled exception.
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
delayRun
would make more sense here, since it will delay the running of the callback function by X seconds. Not a very await-ey example, IMO.
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
await new Promise(resolve => setTimeout(() => { resolve({ data: 'your return data'}) }, 1000))
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 || '')
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)
...
}
promisify
doing to setTimeout
for your custom sleep
function that causes it to no longer need a function as the first argument? For example, if you run setTimeout(5000);
(not having a function as the first argument) you get Uncaught TypeError [ERR_INVALID_CALLBACK]: Callback must be a function. Received 5000
.
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 });
});
This is a quicker fix in one-liner.
Hope this will help.
// WAIT FOR 200 MILISECONDS TO GET DATA //
await setTimeout(()=>{}, 200);
await setTimeout(()=>{console.log('first')}, 200); console.log ('second')
prints second then first
var test = async () => { await setTimeout(()=>{console.log('first')}, 1000); console.log ('second') }
I've extended the timeout to show it's usefulness.
Success story sharing
Promise.all
approach. So simple and elegant!var [parents]
represent? I haven't seen it before and it's a difficult thing to googleasync function
.async
/await
is based on promises. The only thing it replaces arethen
calls.