JavaScript承诺:顺序执行承诺

时间:2019-03-22 09:23:27

标签: javascript es6-promise

为了更清楚地理解诺言,我一直在阅读一些关于它的非常有趣的文章。我遇到了以下代码,这些代码非常适合顺序执行promise。但是我不明白它是如何工作的。

 function doFirstThing(){
   return new Promise(function(resolve,reject){
       setTimeout(()=>{
           resolve(1);
       },1000)
   })
 }

 function doSecondThing(res){
   return new Promise(function(resolve,reject){
       setTimeout(()=>{
           resolve(res + 1);
       },1000)
   })
 }

 function doThirdThing(res){
   return new Promise(function(resolve,reject){
       setTimeout(()=>{
           resolve(res + 2);
       },1000)
   })
 }
 promiseFactories = [doFirstThing, doSecondThing, doThirdThing];

 function executeSequentially(promiseFactories) {
         var result = Promise.resolve(); // this is the most problematic line 
               promiseFactories.forEach(function (promiseFactory) {
               result = result.then(promiseFactory);// what is happening here ?
    });
    return result;
 }

 executeSequentially(promiseFactories)

我确实知道,承诺一旦创建便会立即执行。由于某种原因,我无法理解执行流程。特别是以下代码:

var result = Promise.resolve()//and empty promise is created.

如果有人可以帮助我理解在空promise的'then'方法内部调用promiseFactory方法是如何使其依次执行的,就像这样。还是因为forEach循环?

result = result.then(promiseFactory);

我尝试用“地图”功能替换“ forEach”,但仍然产生相同的结果。即按顺序执行的方法。 另外,该值如何从一个链接函数传递给另一个函数?

任何帮助或文章/博客都受到高度赞赏。

4 个答案:

答案 0 :(得分:3)

如果您希望这样做,总是建议使用Promise.all

function doFirstThing() {
      return new Promise(function(resolve, reject) {
        setTimeout(() => {
          resolve(1);
        }, 1000)
      })
    }
    
    function doSecondThing(res) {
      return new Promise(function(resolve, reject) {
        setTimeout(() => {
          resolve(res + 1);
        }, 1000)
      })
    }
    
    function doThirdThing(res) {
      return new Promise(function(resolve, reject) {
        setTimeout(() => {
          resolve(res + 2);
        }, 1000)
      })
    }
    let promiseFactories = [doFirstThing(2), doSecondThing(1), doThirdThing(3)];
    
    Promise.all(promiseFactories)
      .then(data => {
        console.log("completed all promises", data);
      })

要依次运行它:

function doFirstThing() {
  return new Promise(function(resolve, reject) {
    setTimeout(() => {
      resolve(1);
    }, 1000)
  })
}

function doSecondThing(res) {
  return new Promise(function(resolve, reject) {
    setTimeout(() => {
      resolve(res + 1);
    }, 3000)
  })
}

function doThirdThing(res) {
  return new Promise(function(resolve, reject) {
    setTimeout(() => {
      resolve(res + 2);
    }, 5000)
  })
}
promiseFactories = [doFirstThing, doSecondThing, doThirdThing];

function executeSequentially(promiseFactories) {
  promiseFactories.forEach(function(promiseFactory) {
    promiseFactory(1).then((data) =>  {
    console.log(data)
    });
  });
}

executeSequentially(promiseFactories);

答案 1 :(得分:3)

您可以将Promise成像为带有执行功能的盒子。只要创建了承诺,就开始执行。要获得结果值,您必须打开框。您可以使用then

Promise.resolve(5).then(result => console.log(result)); // prints 5

如果您要链接诺言,可以一一打开框:

Promise.resolve(5)
  .then(result => Promise.resolve(result + 1))
  .then(result => Promise.resolve(result * 2))
  .then(result => console.log(result));  // prints 12

此链接使执行同步(一对一)。

如果您要异步执行几个承诺(不链接结果),则可以使用Promise.all

Promise.all([Promise.resolve(1), Promise.resolve(2), Promise.resolve(3)])
  .then(result => console.log(result));  // prints [1,2,3]

在您的情况下:

Promise.all(promiseFactories).then(result => console.log(result));

使用诺言的另一种方法是await

(async ()=> {
   var res1 = await Promise.resolve(5);
   var res2 = await Promise.resolve(res1 + 1);
   var res3 = await Promise.resolve(res2 * 2);
   console.log(res3); // prints 12
})();

await的工作方式与then类似-它使异步执行变为同步

在您的情况下:

async function executeSequentially(promiseFactories) {
    for (const p of promiseFactories) {
        const result = await p;
        console.log(result);
    } 
}

注意: await开箱即用地将值打包到Promise中:

var res1 = await 5; // same as await Promise.resolve(5)

答案 2 :(得分:1)

executeSequentially方法一个接一个地返回所有Promises。它碰巧遍历了promiseFactory,但是可以写成:

function executeSequentially(promiseFactories) {
  return doFirstThing()
  .then(() => doSecondThing())
  .then(doThirdThing() );
}

是一样的。我们基本上是在返回诺言。

现在,我们要重申一系列承诺。

迭代时,我们需要使用then将当前的Promise附加到前一个。但是forEach不会在每次迭代中公开下一个Promise(或上一个)。但是,我们仍然需要它来保持Promises一对一的链接。因此,result'hack':

function executeSequentially(promiseFactories) {
  var result = Promise.resolve(); /*We need a thing that keeps yelling 
  the previous promise in every iteration, so we can keep chaining.
  This 'result' var is that thing. This is keeping a Promise in every
  iteration that resolves when all the previous promises resolve
  sequentially. Since we don't have a Promise in the array
  previous to the first one, we fabricate one out of 'thin air'
  with Promise.resolve() */
  promiseFactories.forEach(function (promiseFactory) {
    result = result.then(promiseFactory); /* Here result is update
    with a new Promise, with is the result of  chaining `result`
    with the current one. Since `result` already had all the previous ones,
    at the end, `result` will be a Promise that depends upon all the
    Promises resolution.*/
  });
return result;
}

现在,还有一个语法怪癖可能使您感到困惑:

result = result.then(promiseFactory);

此行与以下内容几乎相同:

result = result.then(resolvedValue => promiseFactory(resolvedValue));
  

如果有人可以帮助我理解在空promise的'then'方法内部调用promiseFactory方法是如何使其依次执行的,就像这样。还是因为forEach循环?

首先,promiseFactory在这里是一个非常糟糕的名字。该方法应更好地编写如下:

function executeSequentially(promises) {
  var result = Promise.resolve(); // this is the most problematic line 
        promises.forEach(function (currentPromise) {
        result = result.then(currentPromise);// what is happening here ?
});
return result;
}

所以:

  

如何在空promise的'then'方法内调用currentPromise方法以使其顺序执行?

它使顺序执行,因为当您通过then将一个Promise附加到另一个时,它会顺序执行。是then的事情,它与我们遍历Promises的事实完全无关。在迭代之外使用简单的Promises,其工作原理几乎相同:

Promise.resolve() // fake Promises that resolves instanly
.then(fetchUsersFromDatabase) // a function that returns a Promise and takes
// like 1 second. It won't be called until the first one resolves
.then(processUsersData) // another function that takes input from the first, and
// do a lot of complex and asynchronous computations with data from the previous promise.
// it won't be called until `fetchUsersFromDatabase()` resolves, that's what
// `then()` does.
.then(sendDataToClient); // another function that will never be called until
// `processUsersData()` resolves

答案 3 :(得分:1)

如果我们布置了foreach循环,它将看起来如下所示

function doFirstThing(){
   return new Promise(function(resolve,reject){
       setTimeout(()=>{
       console.log(1);
           resolve(1);
       },1000)
   })
 }

 function doSecondThing(res){
   return new Promise(function(resolve,reject){
       setTimeout(()=>{
       console.log(2);
           resolve(res + 1);
       },2000)
   })
 }

 function doThirdThing(res){
   return new Promise(function(resolve,reject){
       setTimeout(()=>{
       console.log(3);
           resolve(res + 2);
       },3000)
   })
 }


Promise.resolve()
       .then(doFirstThing())
       .then(doSecondThing())
       .then(doThirdThing());

  

我确实知道,承诺一旦创建便会立即执行。由于某种原因,我无法理解执行流程。特别是下面这行:   var result = Promise.resolve()//and empty promise is created.

这仅仅是为了掌握承诺链的起点。这是一个已经解决的承诺。为了更好地理解它,您可以使用您的一个承诺来获得如下所示的承诺链。

let promiseFactories= [doSecondThing, doThirdThing];

let result = doFirstThing();

promiseFactories.forEach(function (promiseFactory) {
     result = result.then(promiseFactory);
});

这也将起作用。