[英]Combination of async function + await + setTimeout
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.问题是,我的 while 循环运行得太快,脚本每秒向 google API 发送太多请求。因此我想构建一个 sleep function 来延迟请求。 Thus I could also use this function to delay other requests.
因此我也可以使用这个 function 来延迟其他请求。 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.
请求的响应在 setTimeout 内返回给匿名异步 function,但我不知道如何将响应返回给睡眠 function resp。 to the initial asyncGenerator function.
到初始的 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.我已经尝试了一些选项:将响应存储在全局变量中并从睡眠 function 返回它,匿名 function 中的回调等。
Your sleep
function does not work because setTimeout
does not (yet?) return a promise that could be await
ed.您的
sleep
功能不起作用,因为setTimeout
还没有(还?)返回一个可以await
的承诺。 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.顺便说一句,要减慢您的循环速度,您可能不想使用接收回调并像这样推迟它的
sleep
函数。 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.这让
parents
的计算至少需要 5 秒。
快速单行、内联方式
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 7.6 开始,您可以将 utils 模块中的
promisify
函数与setTimeout()
。
const sleep = require('util').promisify(setTimeout)
const sleep = m => new Promise(r => setTimeout(r, m))
(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. setTimeout
不是async
函数,因此您不能将它与 ES7 async-await 一起使用。 But you could implement your sleep
function using ES6 Promise :但是您可以使用 ES6 Promise实现您的
sleep
功能:
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:然后你就可以在 ES7 async-await 中使用这个新的
sleep
功能:
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.请注意,我只是回答您关于将 ES7 async/await 与
setTimeout
相结合的问题,尽管它可能无法解决您每秒发送过多请求的问题。
Update: Modern node.js versions has a buid-in async timeout implementation, accessible via util.promisify helper:更新:现代 node.js 版本有一个内置的异步超时实现,可通过util.promisify助手访问:
const {promisify} = require('util');
const setTimeoutAsync = promisify(setTimeout);
await setTimeout
finally arrived with Node.js 16, removing the need to use util.promisify()
: await setTimeout
终于在 Node.js 16 中出现,不再需要使用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)官方 Node.js 文档: Timers Promises API (已在 Node 中内置库)
If you would like to use the same kind of syntax as setTimeout
you can write a helper function like this:如果您想使用与
setTimeout
相同类型的语法,您可以编写这样的辅助函数:
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我做了一个要点: https : //gist.github.com/DaveBitter/f44889a2a52ad16b6a5129c39444bb57
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这是我现在在 2020 年在 AWS labdas 中使用 nodejs 的版本
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 new Promise(resolve => setTimeout(() => { resolve({ data: 'your return data'}) }, 1000))
await setTimeout(()=>{}, 200);
如果您的 Node 版本是 15 及更高版本,则可以使用。
The following code works in Chrome and Firefox and maybe other browsers.以下代码适用于 Chrome 和 Firefox,也可能适用于其他浏览器。
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..."
但是在 Internet Explorer 中,我收到
"(resolve **=>** setTimeout..."
的语法错误)
Made a util inspired from Dave 's answer从Dave的答案中获得灵感
Basically passed in a done
callback to call when the operation is finished.基本上传入一个
done
回调函数在操作完成时调用。
// 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);
}
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 leave this code snippet here for someone who wants fetch API call (eg get clients) with setTimeout
:我将这段代码片段留给想要使用
setTimeout
获取 API 调用(例如获取客户端)的人:
const { data } = await new Promise(resolve => setTimeout(resolve, 250)).then(() => getClientsService())
setName(data.name || '')
setEmail(data.email || '')
I would like to point out a robust extension to Promise.all
.我想指出一个对
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))
).与一个 promise 一起工作的一个相当优雅的解决方案是只限时使用 promise 超时(例如
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
myPromise
然后可以在内部等待不同的超时,或者简单地使用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.结果是一个异步调用,每秒运行的频率不超过两次,如果出现某些(网络/服务器?)错误,超时时间为 5 秒。
Moreover, you can make this very versatile and customizable function as such:此外,您可以使这个非常通用和可定制的 function 像这样:
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.最终,您可以自定义超时时间以及 promise 应该成功还是超时。 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)
您还可以设置一个字符串而不是 boolean 作为
throws
并将此变量绑定到reject
自定义错误消息: reject.bind(undefined, throws)
Note that you should not pass your promise with await:请注意,您不应将 promise 与 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:有了标记的答案,我有一个 lint 错误 [no-promise-executor-return] 所以我在这里找到了更正的版本,使用大括号来明确表示不返回任何东西的意图:
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);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.