如何连续执行承诺?

时间:2017-05-24 16:42:29

标签: javascript asynchronous promise

var promiseReturningFuncs = [];
for(var i = 0; i < 5; i++){
  promiseReturningFuncs.push(askQuestion);
}

var programmers = [];
Promise.reduce(promiseReturningFuncs, function(resp, x) {
  console.log(typeof resp);
  if(typeof resp != "function") {
    programmers.push(resp);
  }
  return x();
})
.then(function(resp) {
  programmers.push(resp);
  console.log(programmers);
});

我的目标:串行执行askQuestion函数并解析该函数​​创建的对象数组。 (此函数必须串行执行,以便它可以响应用户输入)

假设askQuestion函数返回一个promise,它解析了我想要添加到数组的对象。

这是我这样做的混乱方式。 我希望找到一种更干净的方法来做到这一点,理想情况下,我甚至不需要推送到一个数组,我只需要一个最终的.then,其中响应是一个数组。

4 个答案:

答案 0 :(得分:4)

由于您似乎正在使用Bluebird promise库,因此您可以使用许多内置选项来对承诺返回函数进行排序。您可以将Promise.reduce()Promise.map()与并发值1,Promise.mapSeriesPromise.each()一起使用。如果迭代器函数返回一个promise,那么所有这些都将等待下一次迭代,直到该promise被解析。使用哪种方法更多地取决于数据结构的机制以及您想要的结果(实际上您没有显示或描述的结果)。

假设您有一系列承诺返回函数,并且您希望一次调用一个函数,等待一个函数在调用下一个函数之前解析。如果您想要所有结果,那么我建议Promise.mapSeries()

let arrayOfPromiseReturningFunctions = [...];

// call all the promise returning functions in the array, one at a time
// wait for one to resolve before calling the next
Promise.mapSeries(arrayOfPromiseReturningFunctions, function(fn) {
    return fn();
}).then(function(results) {
     // results is an array of resolved results from all the promises
}).catch(function(err) {
     // process error here
});
也可以使用

Promise.reduce(),但它会累积一个结果,将其从一个结果传递到下一个结果,并以最终结果结束(如Array.prototype.reduce()那样)。

Promise.map()Promise.mapSeries()的更通用版本,可让您控制并发数(同时在飞行中的异步操作数)。

Promise.each()也会对您的函数进行排序,但不会累积结果。它假设您要么没有结果,要么您正在带外或通过副作用累积结果。我倾向于不喜欢使用Promise.each(),因为我不喜欢副作用编程。

答案 1 :(得分:2)

您可以使用ES6(ES2015)功能在纯JS中解决此问题:

function processArray(arr, fn) {
    return arr.reduce(
        (p, v) => p.then((a) => fn(v).then(r => a.concat([r]))),
        Promise.resolve([])
    );
}

它应用给阵列串联的​​函数并解析为结果数组

用法:

const numbers = [0, 4, 20, 100];
const multiplyBy3 = (x) => new Promise(res => res(x * 3));

// Prints [ 0, 12, 60, 300 ]
processArray(numbers, multiplyBy3).then(console.log);

您需要仔细检查浏览器兼容性,但这适用于当前合理的Chrome(v59),NodeJS(v8.1.2)以及其他大多数其他产品。

答案 2 :(得分:0)

使用递归函数(以非承诺方式)一个接一个地执行:

(function iterate(i,result,callback){
 if( i>5 ) callback(result);askQuestion().then(res=>iterate(i+1,result.concat([res]),callback);
})(0,[],console.log);

对于舒尔来说,这可以包含在一个承诺中:

function askFive(){
return new Promise(function(callback){
(function iterate(i,result){
 if( i>5 ) callback(result);askQuestion().then(res=>iterate(i+1,result.concat([res]),callback);
})(0,[],console.log);
});
}

askFive().then(console.log);

或者:

function afteranother(i,promise){
   return new Promise(function(resolve){
     if(!i) return resolve([]);
     afteranother(i-1,promise).then(val=>promise().then(val2=>resolve(val.concat([val2])));
   });
}

afteranother(5,askQuestion).then(console.log);

答案 3 :(得分:0)

您可以使用递归,以便可以移动到then块中的下一次迭代。

function promiseToExecuteAllInOrder(promiseReturningFunctions /* array of functions */) {
  var resolvedValues = [];

  return new Promise(function(resolve, reject) {
    function executeNextFunction() {
      var nextFunction = promiseReturningFunctions.pop();
      if(nextFunction) {
        nextFunction().then(function(result) {
          resolvedValues.push(result);
          executeNextFunction();
        });
      } else {
        resolve(resolvedValues);
      }
    }
    executeNextFunction();
  }
}