【问题标题】:How to setTimeout on async await call node如何在异步等待调用节点上设置超时
【发布时间】:2018-10-10 00:54:39
【问题描述】:

如何将 setTimeout 添加到我的异步等待函数调用中?

我有

    request = await getProduct(productids[i]);

在哪里

const getProduct = async productid => {
        return requestPromise(url + productid);
   };

我试过了

    request = await setTimeout((getProduct(productids[i])), 5000);

并得到错误TypeError: "callback" argument must be a function,这是有道理的。该请求位于循环内部,这使我达到了 api 调用的速率限制。

exports.getProducts = async (req, res) => {
  let request;
  for (let i = 0; i <= productids.length - 1; i++) {
    request = await getProduct(productids[i]);
    //I want to wait 5 seconds before making another call in this loop!
  }
};

【问题讨论】:

  • 这是一个非常相似的问题:How to make nightmare request timeout.
  • 你的意思是请求从现在开始执行 1 秒还是你立即开始它,如果它没有在 1 秒内完成,那么你就超时了?
  • 仅供参考,request-promise 库已经支持超时选项,所以如果您只想超时等待响应,您可以使用 request-promise 库中已经内置的选项。
  • @jfriend00 感谢 cmets。我实际上想在函数中放置一个等待,以便在一个请求之后它会在发出另一个请求之前等待。我之前在库中没有看到超时选项,感谢您提供链接。
  • 该超时选项不是下一个请求的等待选项。这些是完全不同的东西。

标签: node.js callback async-await settimeout


【解决方案1】:

从 Node 15 及更高版本开始,有新的Timers Promises API 让您避免构建包装:

import {
  setTimeout,
  setImmediate,
  setInterval,
} from 'timers/promises';

console.log('before')
await setTimeout(1000)
console.log('after 1 sec')

所以你可以用异步迭代器来写你的问题:

import {
  setTimeout
} from 'timers/promises'

async function getProducts (req, res) {
  const productids = [1, 2, 3]

  for await (const product of processData(productids)) {
    console.log(product)
  }
}

async function * processData (productids) {
  while (productids.length > 0) {
    const id = productids.pop()
    const product = { id }
    yield product
    await setTimeout(5000)
  }
}

getProducts()

【讨论】:

    【解决方案2】:

    从节点 v15 开始,您可以使用 Timers Promises API:

    const timersPromises = require('timers/promises');
    
    async function test() {
      await timersPromises.setTimeout(1000);
    }
    
    test();
    

    请注意,此功能是实验性的,可能会在未来的版本中发生变化。

    【讨论】:

      【解决方案3】:

      您可以使用一个简单的小函数,该函数返回一个延迟后解决的承诺:

      function delay(t, val) {
         return new Promise(function(resolve) {
             setTimeout(function() {
                 resolve(val);
             }, t);
         });
      }
      

      然后,await 在你的循环中:

      exports.getProducts = async (req, res) => {
        let request;
        for (let id of productids) {
          request = await getProduct(id);
          await delay(5000);
        }
      };
      

      注意:我还将您的 for 循环切换为使用 for/of,这不是必需的,但比您使用的要干净一些。

      【讨论】:

      • 谢谢!这是完美的,感谢您提供示例用法。也感谢 for 循环的变化,不知道你能做到这一点,所以我从这个答案中学到了两倍。
      • 我有一个问题,你知道使用 await 的异步方法是否在等待一个 api 调用完成后再进行下一个调用?如果是,那么这个延迟是必要的,如果不是,我可能需要找到一种方法来进行系列调用,以避免在这样的延迟中硬编码。
      • @jenryb - 如果getProduct(id) 返回一个仅在异步操作完成时才解析的promise,那么await getProduct(id) 确实会等待它完成并且for 循环确实会等待每次致电getProduct(id)。但是(这很重要),您导出的函数 getProducts() 不会等待返回。它立即返回并返回一个当for 循环和所有await 操作都完成时解决的承诺。
      【解决方案4】:

      实际上,我有一段非常标准的代码可以用来做这件事:

      function PromiseTimeout(delayms) {
          return new Promise(function (resolve, reject) {
              setTimeout(resolve, delayms);
          });
      }
      

      用法:

      await PromiseTimeout(1000);
      

      如果您使用的是 Bluebird Promise,则它内置为 Promise.timeout

      更多问题:您检查过 API 文档吗?一些 API 会告诉您在下一个请求之前必须等待多少时间。或者允许大批量下载数据。

      【讨论】:

      • 在 Bluebird 中,它是 Promise.delay 而不是 Promise.timeout。
      猜你喜欢
      • 1970-01-01
      • 2021-10-12
      • 1970-01-01
      • 2023-03-27
      • 2016-09-04
      • 1970-01-01
      • 2018-12-30
      相关资源
      最近更新 更多