【问题标题】:Add a delay or sleep to an array of promises in nodejs [duplicate]向nodejs中的一系列承诺添加延迟或睡眠[重复]
【发布时间】:2018-05-22 01:00:20
【问题描述】:

我一直在努力解决一个承诺链问题。我调用了一个外部 api,它返回我需要处理和摄取到 mongo 数据库中的数据。我正在使用 nodejs 和 mongodb 和 express。无论如何,对 api 的调用工作正常,问题是我一次制作了大量的它们。我想放慢他们的速度,就像为一组打电话一样。等一下。为下一组打出所有电话。如果这是已知数量的集合,我会承诺将它们链接起来。我不知道有多少套,所以我正在循环播放它们。我认为关闭是问题,但无法解决。继续示例代码!

  function readApi(carFactory){
    var promise = new Promise(function(resolve, reject) {
      // call out to api, get set of car data from factory1

      console.log(carFactory);
      if (true) {
        console.log('resolved');
        resolve("Stuff worked!"+carFactory);
      }
      else {
        reject(Error("It broke"));
      }
    });
    return promise;
  }

  function manager(){

    //singular call
    readApi("this is a singular test").then(returnedThing=>{
      console.log(returnedThing); //Stuff worked! this is a singular test
    });

    let dynamicList = ["carFactory1", "carFactory2","carFactory3","carFactory..N"];
    let readApiAction = [];
    dynamicList.forEach(carIter=>{
      readApiAction.push(readApi(carIter));
    });
    //ok so now I got an array of promise objects.
    //I want to call the first one, wait 1 minute and then call the next one. 

    //originally I was calling promise.all, but there is no way to get at 
    //each promise to pause them out so this code is what I am looking to fix
    let results= Promise.all(readApiAction);
    results.then(data=>{
      data.forEach(resolvedData=>{
        console.log(resolvedData); //Stuff worked carFactory1, etc... 
      });      
    });


    //singular call with timeout, this does not work, each one called at the same time
    let readApiActionTimeouts = [];
    dynamicList.forEach(carIter=>{
      setTimeout(callingTimeout(carIter), 6000);
    });
  }

  //just a function to call the promise in a timeout
  //this fails with this  - TypeError: "callback" argument must be a function
  function callingTimeout(carIter){
    readApi(carIter).then(returnedThing=>{
      console.log("timeout version"+returnedThing);
    });
  }

【问题讨论】:

标签: javascript node.js callback promise async-await


【解决方案1】:

有点理论。原生 Promise.all 只是分组承诺。它们仍然同时执行(尽管以异步方式,作为所有 JS 代码,但彼此同时执行)。这意味着它仍然会拥塞 API 并执行大量调用。

另外需要注意的是,如果你想延迟一个承诺,你必须延迟它的返回值(例如resolve)。为此,您可以使用 setTimeout INSIDE new Promise(请看下面的更多说明)。

设置超时是异步的。它不像其他语言那样工作(它不只是暂停执行)。在您的代码中设置固定超时只是导致将所有执行移动 6 秒。它们仍然并行发生(虽然在不同的滴答声中,但差异很小)。尝试例如为循环中的每个超时生成不同的超时 - 您会注意到它们发生在不同的时间但是!这对于 Promisified 代码来说不是一个好的解决方案!

现在 - 是时候给出实际答案了!

如果你使用 Bluebird,它有一个特殊的方法来为每个 Promise 添加延迟或超时。如果没有它,您将不得不围绕 Promise 编写一个包装器,例如在特定时间后解决它,然后将其与 Promise.all 一起使用。

第一个解决方案(蓝鸟):

function delayMyPromise(myPromise, myDelay);
  return Promise.delay(myDelay).then(function() {
    return myPromise;
  });
});

然后在你的代码中:

return Promise.all(delayMyPromise(promise1, 1000), delayMyPromise(promise2, 2000)); // Notice different delays, you may generate them programatically

或者更酷的是,您可以使用 Bluebird 中的 Promise.map 而不是具有特殊并发设置的 Promise.all,因此您可以强制您的 Promise 以特定顺序执行,例如一次 2 个。 这就是我在之前的项目中的做法:)

更多:

纯原生 Promise 实现:

function delayMyPromise(myPromise, myDelay) {
  return new Promise(function (resolve, reject) {
    setTimeout(function() {
      return resolve(myPromise);
    }, myDelay);
  });
}

但是,如果您不介意使用 Bluebird,我强烈推荐第一种方法。就像lodash 的 Promises 一样,它真的很快:)

【讨论】:

  • 感谢您的回答,我会试一试并回复您
  • 信守承诺,这样我就不必添加另一个库,再次感谢!
  • setTimeout 方法的问题是,如果你有几个这样的延迟承诺与Promise.all() 链接,那么如果第一个失败 - 其他人仍然会在计时器运行后触发,即使它们不应该,因为如果其中一个承诺被拒绝,all 就会中断。你知道bluebird是否解决了这个问题?
  • 由于 Javascript 会热切地评估传递的承诺,我认为 delayMyPromise 的参数应该是一个返回承诺的 lambda,然后在 delayMyPromise 中调用 resolve(myPromise())。
【解决方案2】:

你得到错误:TypeError: "callback" argument must be a function 因为你的callingTimeout 什么都不返回,setTimeout 需要一个函数作为参数,这是解决它的方法:

    let readApiActionTimeouts = [];
    dynamicList.forEach(carIter=>{
          callingTimeout(carIter)
    });

你的承诺:

function readApi(carFactory){
    var promise = new Promise(function(resolve, reject) {
       //...
       setTimeout(()=>{
          resolve("Stuff worked!"+carFactory);
       }, 6000);
       //...
    });
    return promise;
  }

【讨论】:

  • 所以如果我的列表中只有一个,它会等待一分钟然后解决正确吗?
  • 是的,你是对的
  • @spartikus 您可以添加新变量以了解列表中是否有一个或多个
【解决方案3】:

你可以对这样的事情使用递归。

当您调用.forEach 时,每次迭代都会立即发生。

在下面的示例中,doSomething 在 setTimeout 发生之前不会被调用,这意味着每个字母间隔 1 秒打印。

let letters = ["a", "b", "c"];

function doSomething(arr) {
  console.log(arr[0]);
  if (arr.length > 1) {
    setTimeout(() => doSomething(arr.slice(1)), 1000);
  }
}

doSomething(letters);

或者,对于您的一系列承诺:

let promises = [
  Promise.resolve("A"),
  Promise.resolve("B"),
  Promise.resolve("C"),
];

function doSomething(arr) {
  arr[0].then((result) => {
    console.log(result);
    if (arr.length > 1) {
      setTimeout(() => doSomething(arr.slice(1)), 1000);
    }
  })
}

doSomething(promises);

【讨论】:

  • 如果字母是一系列承诺,这会起作用吗?那么promise的“then”应该在哪里调用呢?
  • 我已经更新了一个例子
猜你喜欢
  • 1970-01-01
  • 2021-09-05
  • 2016-12-21
  • 1970-01-01
  • 1970-01-01
  • 2013-03-03
  • 2012-04-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多