【问题标题】:JavaScript Promises: Executing Promises SequentiallyJavaScript Promises:按顺序执行 Promise
【发布时间】:2019-03-22 09:23:26
【问题描述】:

为了更清楚地理解 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.

如果有人可以帮助我理解如何在空承诺的“then”方法中调用 promiseFactory 方法使其按顺序执行,就像这样。还是因为 forEach 循环?

result = result.then(promiseFactory);

我尝试将 'forEach' 替换为 'map' 函数,但仍然产生了相同的结果。即按顺序执行的方法。 另外,值如何从一个链式函数传递到另一个?

非常感谢任何帮助或文章/博客。

【问题讨论】:

    标签: javascript es6-promise


    【解决方案1】:

    如果您想要这样的行为,始终建议使用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);

    【讨论】:

    • Promise.all() 并行运行这些方法,而不是按顺序运行。
    • @NidhinRaj 为此添加了代码。检查更新的答案
    • 谢谢。我正在寻找有关它如何按顺序运行的解释。为什么 var result = Promise.resolve();在循环之外。为什么循环中的 Promise.resolve().then(promiseFactory) 不起作用?另外,尝试将所有工厂的超时值设置为 1000。你会发现所有的 Promise 都是并行运行的。
    • @NidhinRaj 我们不需要var result = Promise.resolve();
    • @NidhinRaj 如果超时为 1 秒,那么它会将这些超时初始化为 1 秒,并且所有承诺在 1 秒内同时解决。所以为了让这些看起来顺序我改变了超时值
    【解决方案2】:

    你可以把 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(你不链接结果),你可以使用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));
    

    如何使用 Promise 的另一种选择是 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)
    

    【讨论】:

    • 感谢您的回复。那么理解使用 await 在循环内执行 promise 确保它们按顺序执行是否安全?
    • 是的,当然。 await 同步解析承诺。
    • 另外,@ttulka 你同意 promise.all() 总是并行执行吗?顺便说一句,感谢您提供简洁明了的解释 :-)
    • 是的,Promise.all 异步等待所有的 Promise,然后将解析的结果返回到一个数组中,该数组按与参数相同的顺序排列。
    【解决方案3】:

    executeSequentially 方法一个接一个地返回所有 Promise。它恰好迭代了promiseFactory,但它可以写成:

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

    它是一样的。我们基本上是在返回一个 Promise。

    然而,现在,我们想要迭代一组 Promise。

    在迭代时,我们需要用then 将当前的 Promise 附加到前一个 Promise 上。但是forEach 不会在每次迭代中暴露下一个 Promise 或上一个 Promise。然而我们仍然需要它来一个一个地链接 Promise。因此,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));
    

    如果有人可以帮助我理解如何在空承诺的“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 附加到另一个 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
    

    【讨论】:

    • 感谢您的回答。现在我清楚地理解了代码 sn-p。
    【解决方案4】:

    如果我们布置 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.

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

    let promiseFactories= [doSecondThing, doThirdThing];
    
    let result = doFirstThing();
    
    promiseFactories.forEach(function (promiseFactory) {
         result = result.then(promiseFactory);
    });
    

    这也可以。

    【讨论】:

    • 感谢您的回答。这是我试图理解的重点。关于您的代码,如果我们将所有超时设置为 1000,那么似乎所有的 Promise 都是同时运行的。它们只有在循环中才能按顺序运行。我说的对吗?
    • 正确,因为循环创建了一个动态的承诺链,它将按顺序运行。
    猜你喜欢
    • 2020-01-03
    • 1970-01-01
    • 1970-01-01
    • 2018-07-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-07
    相关资源
    最近更新 更多