async函数+await+setTimeout的组合

IT技术 javascript async-await settimeout ecmascript-2017
2021-01-23 08:21:10

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

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

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

6个回答

您的sleep函数不起作用,因为setTimeout还没有(还?)返回一个可以被await编辑的Promise您需要手动Promise它:

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方法。如此简单和优雅!
2021-03-19 08:21:10
@NateUsher 这是数组解构
2021-03-22 08:21:10
@tinkerr 如果需要等待超时,则需要将其声明为异步 - 不。一个函数只需要返回一个可以被等待的Promise(或者实际上,一个 thenable 就足够了)。它如何实现取决于函数的实现,它不需要是async function.
2021-03-26 08:21:10
的符号var [parents]代表什么?我以前没见过,谷歌很难
2021-03-29 08:21:10
@naisanza没有,async/await基于Promise。它唯一取代的是then调用。
2021-04-07 08:21:10

快速的单行内联方式

 await new Promise(resolve => setTimeout(resolve, 1000));
@PabloDK它也可以这样表示:await new Promise((resolve, reject) => setTimeout(resolve, 1000));所以,resolvereject当你创建一个无极暴露回调。你只是告诉setTimeout执行resolve()
2021-03-12 08:21:10
@PabloDK 会永远阻塞,因为Promise永远不会解决。
2021-03-21 08:21:10
当你们在同一行中使用 "resolve" x 2 次是什么意思?比如: await new Promise(resolve => setTimeout(resolve, 1000)); 它参考吗?对自己还是什么?我会做这样的事情:function myFunc(){}; 等待新的Promise(解决 => setTimeout(myFunc,1000));
2021-04-02 08:21:10
@PabloDK您可以将上面的单行扩展到这个(更详细的)版本,这希望能清楚地说明为什么resolve出现两次。如果它仍然令人困惑,请查看PromiseMDN 文档
2021-04-07 08:21:10

从 Node 7.6 开始,您可以将promisifyutils module中的函数功能与setTimeout().

节点.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")
})()
我已经删除了单行,因为答案就在下面,但是我看到许多流行的答案更新了他们的答案以包含其他新答案,因为大多数读者都懒得看前几个回复。
2021-03-14 08:21:10
嘿,@Harry。您似乎在自己的答案中加入了 FlavorScape 的答案中的一个衬垫。我不想推测你的意图,但这对他们来说并不公平。你能回滚你的编辑吗?现在看起来有点像抄袭。。
2021-03-19 08:21:10
require 版本显然比getOwnPropertySymbols版本好得多......如果它没有坏......!
2021-03-21 08:21:10
在 nodeJSawait require('util').promisify(setTimeout)(3000)中也可以通过以下方式实现而不需要:await setTimeout[Object.getOwnPropertySymbols(setTimeout)[0]](3000)
2021-03-22 08:21:10
有趣的@Shl。不过,我认为它的可读性不如我的解决方案。如果人们不同意,我可以将其添加到解决方案中吗?
2021-03-31 08:21:10

setTimeout不是一个async函数,所以你不能在 ES7 async-await 中使用它。但是您可以sleep使用 ES6 Promise实现您的功能

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

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

var fileList = await sleep(listFiles, nextPageToken)

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


更新:现代 node.js 版本有一个内置的异步超时实现,可通过util.promisify助手访问

const {promisify} = require('util');
const setTimeoutAsync = promisify(setTimeout);
@Bergi 我认为它会冒泡到new Promise你可以sleep.catch做到的地方。
2021-03-12 08:21:10
> 每秒发送太多请求的问题。您可能想使用“去抖动”来防止 UI 触发太多请求。
2021-03-27 08:21:10
@Dodekeract 不,它在异步setTimeout回调中并且new Promise回调已经完成了很长时间。它将冒泡到全局上下文并作为未处理的异常抛出。
2021-03-31 08:21:10
你不应该这样做,当fn抛出错误时不会被捕获。
2021-04-09 08:21:10

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 中构建的库)