在nodejs中为promises数组添加延迟或睡眠[重复]

问题描述 投票:1回答:3

这个问题在这里已有答案:

我有一个我一直在努力解决的承诺链问题。我打电话给外部api,它返回我需要处理和摄取到mongo数据库的数据。我正在使用nodejs和mongodb。无论如何,对api的调用工作正常,问题是我正在制作大量的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
3个回答
4
投票

有点理论。本土的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,这样您就可以强制您的承诺以特定顺序执行,例如一次2个。这就是我在以前的项目中做到的:)

更多信息:

纯粹的本机Promise实现:

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

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


1
投票

你得到错误: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;
  }

1
投票

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

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

在下面的示例中,在发生setTimeout之前不会调用doSomething,这意味着每个字母相隔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);
© www.soinside.com 2019 - 2024. All rights reserved.