【问题标题】:Combination of async function + await + setTimeoutasync 函数 + await + setTimeout 的组合
【发布时间】:2016-01-22 05:53:30
【问题描述】:

我正在尝试使用新的异步功能,我希望解决我的问题能在未来帮助其他人。这是我正在工作的代码:

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

问题是,我的 while 循环运行速度太快,脚本每秒向 google API 发送的请求太多。因此,我想构建一个延迟请求的睡眠功能。因此我也可以使用这个函数来延迟其他请求。如果有其他方法可以延迟请求,请告诉我。

无论如何,这是我的新代码,它不起作用。请求的响应在 setTimeout 内返回给匿名异步函数,但我只是不知道如何将响应返回给睡眠函数 resp。到初始的 asyncGenerator 函数。

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

我已经尝试了一些选项:将响应存储在全局变量中并从睡眠函数中返回,在匿名函数中回调等。

【问题讨论】:

    标签: javascript async-await settimeout ecmascript-2017


    【解决方案1】:

    您的sleep 函数不起作用,因为setTimeout 没有(还没有?)返回一个可能是awaited 的承诺。您需要手动承诺:

    function timeout(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }
    async function sleep(fn, ...args) {
        await timeout(3000);
        return fn(...args);
    }
    

    顺便说一句,要减慢循环速度,您可能不想使用sleep 函数,该函数接受回调并像这样推迟它。我推荐:

    while (goOn) {
      // other code
      var [parents] = await Promise.all([
          listFiles(nextPageToken).then(requestParents),
          timeout(5000)
      ]);
      // other code
    }
    

    这让parents 的计算至少需要 5 秒。

    【讨论】:

    • 喜欢Promise.all 方法。如此简单优雅!
    • var [parents] 的符号代表什么?我以前没见过,google 一下很难
    • @NateUsher 这是array destructuring
    • @tinkerr "timeout needs to be declared async if it needs to be awaited" - 不。一个函数只需要返回一个可以等待的promise(或者实际上,一个thenable就足够了)。它如何实现取决于功能的实现,它不需要是async function。
    • @naisanza 不,async/await 是基于的承诺。它唯一替换的是then 调用。
    【解决方案2】:

    快速的单行、内联方式

     await new Promise(resolve => setTimeout(resolve, 1000));
    

    【讨论】:

    • 你们在同一行中使用“resolve”x 2 次是什么意思?比如:await new Promise(resolve => setTimeout(resolve, 1000));它参考吗?对自己还是什么?我会做这样的事情:function myFunc(){}; await new Promise(resolve => setTimeout(myFunc, 1000));
    • @PabloDK 将永远阻塞,因为承诺永远不会解决。
    • @PabloDK 您可以将上面的单行扩展为this (more verbose) version,希望这可以清楚地说明resolve 出现两次的原因。如果仍然感到困惑,请查看MDN docs for Promise。
    • @PabloDK 也可以这样表示:await new Promise((resolve, reject) => setTimeout(resolve, 1000));。因此,resolve 和 reject 是在创建 Promise 时公开的回调。你只是告诉setTimeout 执行resolve()。
    【解决方案3】:

    自 Node 7.6 起,您可以将 utils 模块中的函数 promisify 与 setTimeout() 结合使用。

    Node.js

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

    Javascript

    const sleep = m => new Promise(r => setTimeout(r, m))
    

    用法

    (async () => {
        console.time("Slept for")
        await sleep(3000)
        console.timeEnd("Slept for")
    })()
    

    【讨论】:

    • 在nodeJS中await require('util').promisify(setTimeout)(3000)也可以在不需要的情况下实现:await setTimeout[Object.getOwnPropertySymbols(setTimeout)[0]](3000)
    • 有趣的@Shl。我认为它的可读性不如我的解决方案。如果人们不同意,我可以将其添加到解决方案中吗?
    • require 版本显然比getOwnPropertySymbols 版本好得多......如果它没有坏......!
    • 嘿,@Harry。看来您将 FlavorScape 答案中的一个衬里合并到您自己的答案中。我不想推测你的意图,但这对他们来说并不公平。你能回滚你的编辑吗?现在看起来有点像抄袭..
    • 我已经删除了单行,因为答案就在下面,但是我看到许多流行的答案更新了他们的答案以包含其他新答案,因为大多数读者都不会费心去看前几个回应。
    【解决方案4】:

    setTimeout 不是async 函数,因此您不能将它与 ES7 async-await 一起使用。但是你可以使用 ES6 Promise 来实现你的 sleep 函数:

    function sleep (fn, par) {
      return new Promise((resolve) => {
        // wait 3s before calling fn(par)
        setTimeout(() => resolve(fn(par)), 3000)
      })
    }
    

    然后你就可以在 ES7 async-await 中使用这个新的 sleep 函数了:

    var fileList = await sleep(listFiles, nextPageToken)
    

    请注意,我只是在回答您关于将 ES7 async/await 与 setTimeout 结合使用的问题,尽管它可能无助于解决您每秒发送过多请求的问题。 p>


    更新:现代 node.js 版本具有内置异步超时实现,可通过 util.promisify helper 访问:

    const {promisify} = require('util');
    const setTimeoutAsync = promisify(setTimeout);
    

    【讨论】:

    • 你不应该这样做,当fn抛出错误时不会被捕获。
    • @Bergi 我认为它会冒泡到new Promise,在那里你可以sleep.catch 它。
    • @Dodekeract 不,它在异步 setTimeout 回调中,并且 new Promise 回调已经完成了很长时间。它将冒泡到全局上下文并作为未处理的异常抛出。
    • >每秒发送太多请求的问题。您可能想使用“去抖动”来防止 UI 触发太多请求。
    【解决方案5】:

    2021 年更新

    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"
    })()
    

    Node.js 官方文档:Timers Promises API(Node 中已内置库)

    【讨论】:

      【解决方案6】:

      如果您想使用与setTimeout 相同的语法,您可以编写一个这样的辅助函数:

      const setAsyncTimeout = (cb, timeout = 0) => new Promise(resolve => {
          setTimeout(() => {
              cb();
              resolve();
          }, timeout);
      });
      

      然后你可以这样称呼它:

      const doStuffAsync = async () => {
          await setAsyncTimeout(() => {
              // Do stuff
          }, 1000);
      
          await setAsyncTimeout(() => {
              // Do more stuff
          }, 500);
      
          await setAsyncTimeout(() => {
              // Do even more stuff
          }, 2000);
      };
      
      doStuffAsync();
      

      我做了一个要点:https://gist.github.com/DaveBitter/f44889a2a52ad16b6a5129c39444bb57

      【讨论】:

      • 像delayRun 这样的函数名称在这里更有意义,因为它会将回调函数的运行延迟 X 秒。不是一个非常等待的例子,IMO。
      【解决方案7】:
      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
      

      【讨论】:

        【解决方案8】:

        这是我现在在 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)
            ...
        }
        

        【讨论】:

        • promisify 对 setTimeout 的自定义 sleep 函数做了什么,导致它不再需要函数作为第一个参数?例如,如果你运行setTimeout(5000);(没有函数作为第一个参数)你会得到Uncaught TypeError [ERR_INVALID_CALLBACK]: Callback must be a function. Received 5000。
        【解决方案9】:
        await new Promise(resolve => setTimeout(() => { resolve({ data: 'your return data'}) }, 1000))
        

        【讨论】:

          【解决方案10】:
          await setTimeout(()=>{}, 200);
          

          如果您的 Node 版本为 15 及以上,则可以使用。

          【讨论】:

          【解决方案11】:

          根据Dave 的answer 灵感制作了一个实用程序

          基本上是传入一个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);
          });
          

          我就是这样使用它的:

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

          【讨论】:

            【解决方案12】:

            以下代码适用于 Chrome 和 Firefox 以及其他浏览器。

            function timeout(ms) {
                return new Promise(resolve => setTimeout(resolve, ms));
            }
            async function sleep(fn, ...args) {
                await timeout(3000);
                return fn(...args);
            }
            

            但在 Internet Explorer 中,"(resolve **=>** setTimeout..." 出现语法错误

            【讨论】:

              【解决方案13】:

              如何一次记录所有响应?

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

              【讨论】:

                【解决方案14】:

                这是一种在单行中更快的修复方法。

                希望这会有所帮助。

                // WAIT FOR 200 MILISECONDS TO GET DATA //
                await setTimeout(()=>{}, 200);
                

                【讨论】:

                • 不起作用。这:await setTimeout(()=>{console.log('first')}, 200); console.log ('second') 打印 second 然后 first
                • @gregn3 是的。这是一个非阻塞解决方案,其中函数外部的代码可以继续执行,而“阻塞操作”在主程序流程之外完成。尽管您和 Rommy 和 Mohamad 提供的语法并不完全正确,因为需要在异步函数中敲击等待(可能是最近添加的),但我也在使用 node.js。这是我的调整解决方案。 var test = async () => { await setTimeout(()=>{console.log('first')}, 1000); console.log ('second') }我已经延长了超时时间以显示它的用处。
                猜你喜欢
                • 2018-07-18
                • 2018-09-23
                • 2020-07-26
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多