简体   繁体   English

异步 function + await + setTimeout 的组合

[英]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()

Node.js节点.js

const sleep = require('util').promisify(setTimeout)

Javascript 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. 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);

Update 2021 2021 年更新

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 answerDave答案中获得灵感

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);
}

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 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM