使用Promise.all()履行承诺时执行操作

时间:2016-09-27 15:35:30

标签: javascript asynchronous promise ecmascript-6

我可以使用Promise.all(array)异步解析一堆承诺。但是.then()只有在所有这些承诺得到解决后才能运行。当承诺得到解决时,我如何执行行动?

例如,我想使用Promise.all()以异步方式加载文章中的所有段落 - 这样网络就会立即请求所有火灾。如果段落1完成加载,我希望它呈现到页面 - 但只有在第2段之前加载完成,然后我想要加载段落2。如果段落3已完成加载且2未完成,我希望3在渲染到页面之前等待2。等等。

我试过这样的事情,但我不知道接下来要做什么:

var getStuff = function(number, time){
  return new Promise(function(resolve, reject){
    window.setTimeout(function(){resolve(`${number} - Done.`)}, time);
  });
};

Promise.all([ getStuff(1, 200),
              getStuff(2, 100),
              getStuff(3, 250),
              getStuff(4, 200),
              getStuff(5, 300),
              getStuff(6, 250),
              getStuff(7, 5000)])
.then(function(data){
  console.log(data);
});

如何让数据的控制台日志一个接一个地发生 - 在发出下一个请求之前不用then()解析每个承诺?有更好的方法吗?

4 个答案:

答案 0 :(得分:18)

您无法使用Promise.all实现此顺序,因为从Promise.all返回的承诺会等待所提供的数组中的所有承诺在其自身结算之前同时解析(而不是顺序)。

相反,您可以同时单独创建其请求的承诺和解雇:

// create promises and make concurrent requests
const s1 = getStuff(1, 200);
const s2 = getStuff(2, 100);
const s3 = getStuff(3, 250);
// ...

然后创建一个关于如何处理它们的反应链(stuff2之前的stuff1,stuff3之前的stuff2等)

// create a chain of reaction order to the results of parallel promises
s1
  .then(console.log) // s1 resolved: log result
  .then(() => s2)    // chain s2
  .then(console.log) // s2 resolved: log result
  .then(() => s3)    // chain s3
  // ...
  .then(() => {      // chain another function at at the end for when all promises resolved
    // all promises resolved (all data was logged)
  }

要以承诺创建的相同顺序对承诺结果作出反应,您可以更改getStuff函数,以使用Array.prototype.reduce动态链接反应:

var times = [200, 100, 250, 200, 300, 250, 5000];

var getStuff = function(time, index) { // swap the order of arguments so number is the index passed in from Array.map
  return new Promise((resolve, reject) => {
    window.setTimeout(() => {
      resolve(`${index + 1} - Done.`); // use index + 1 because indexes start at 0
    }, time);
  });
};

times
  // map each time to a promise (and number to the index of that time + 1) and fire of a request
  .map(getStuff)
  // dynamically build a reaction chain for the results of promises
  .reduce((chain, promise) => {
    return chain
      .then(() => promise)
      .then(console.log);
  }, Promise.resolve())
  .then(() => {
    // all promises resolved (all data was logged in order)
  });

答案 1 :(得分:6)

nem035的回答很明显。我想指出,通常在这种情况下,您希望在发生请求时采取相同的操作,并在完成所有操作时采取其他操作。

您可以.all使用.map

Promise.all([ getStuff(1, 200),
            getStuff(2, 100),
            getStuff(3, 250),
            getStuff(4, 200),
            getStuff(5, 300),
            getStuff(6, 250),
            getStuff(7, 5000)]
.map(request => request.then(v => {
   console.log("Request done! Got," v); // or some action per request
   return v;
})).then(data => console.log(data));

您可以使用.map使用您为每个请求使用相同功能的事实来进一步说明这一点:

Promise.all([[1, 200],
            [2, 100],
            [3, 250],
            [4, 200],
            [5, 300],
            [6, 250],
            [7, 5000]])
.map((a, b) => getStuff(a, b))
.map(request => request.then(v => {
   console.log("Request done! Got," v); // or some action per request
   return v;
})).then(data => console.log(data));

进一步:

Promise.all([200, 100, 250, 200, 300, 250, 5000])
.map((a, i) => getStuff(a, i + 1))
.map(request => request.then(v => {
   console.log("Request done! Got," v); // or some action per request
   return v;
})).then(data => console.log(data));

或者蓝鸟:

const sideEffect = v => console.log("Got partial result", v));
const data = [200, 100, 250, 200, 300, 250, 5000];
Promise.map(data, (a, i) => getStuff(a, i + 1).tap(sideEffect))
       .then(data => console.log(data));

当然 - 你应该修复你的后端,要求客户端对数据的不同部分发出7个请求是完全不合理的 - 让后端占用范围。

答案 2 :(得分:2)

我知道它不是原生的,但是对于bluebird,您可以使用Promise.some(在履行count承诺后填充)或Promise.mapSeries(以完整填写承诺)以某种方式实现你期望的流程。

Bluebird API

答案 3 :(得分:0)

正常操作:您可以安全地使用Promise.all()。 promise执行器将并行触发,结果将按照将promises插入promises数组的顺序返回。然后由您自己按照自己喜欢的方式对它们进行排序。比如在下面的代码片段中,我们有五个promises,每个promise将在5秒内随机解析。无论他们的解决时间如何,您都会在最新结果时得到结果;



var promises = [ new Promise( v => setTimeout(_ => v("1st paragraph text"),~~(Math.random()*5000))),
                 new Promise( v => setTimeout(_ => v("2nd paragraph text"),~~(Math.random()*5000))),
                 new Promise( v => setTimeout(_ => v("3rd paragraph text"),~~(Math.random()*5000))),
                 new Promise( v => setTimeout(_ => v("4th paragraph text"),~~(Math.random()*5000))),
                 new Promise( v => setTimeout(_ => v("5th paragraph text"),~~(Math.random()*5000))),
               ];
Promise.all(promises)
       .then(result => console.log(result.reduce((p,c) => p + "\n" + c)));




你想要什么:但是你不想等到最后一个完成,而是想要按顺序处理它们,尽快得到解决。那么Array.prototype.reduce()是你最好的朋友。如



var promises = [ new Promise( v => setTimeout(_ => v("1st paragraph text"),~~(Math.random()*5000))),
                 new Promise( v => setTimeout(_ => v("2nd paragraph text"),~~(Math.random()*5000))),
                 new Promise( v => setTimeout(_ => v("3rd paragraph text"),~~(Math.random()*5000))),
                 new Promise( v => setTimeout(_ => v("4th paragraph text"),~~(Math.random()*5000))),
                 new Promise( v => setTimeout(_ => v("5th paragraph text"),~~(Math.random()*5000)))
               ];
promises.reduce((p,c) => p.then(result => (console.log(result + "\n"),c)))
        .then(result => (console.log(result + "\n")));




请多次运行代码以查看代码的行为方式。文本将尽快更新,但只有在轮到它的情况下才会解决。因此,如果第一个在第二个结束后解决,我们将看到第一个和第二个在他们的订单中同时出现,但他们不会等到第三个解决等等......