【问题标题】:How do you synchronously resolve a chain of es6 promises?您如何同步解决 es6 承诺链?
【发布时间】:2019-08-26 13:47:33
【问题描述】:

我有一个来自库的函数,它返回一个承诺。我需要多次运行这个函数,但每次迭代都必须等到上一个任务完成。

我的假设是我可以做到这一点:

promiseReturner(1)
  .then(promiseReturner(2)
  .then(promiseReturner(3)
  .then(...)

可以使用循环来简化:

var p = Promise.resolve();
for (var i=1; i<=10; i++) {
  p = p.then(promiseReturner(i));
}

但是,当我这样做时,链中的每个承诺都会同时执行,而不是像.then() 似乎暗示的那样一个接一个地执行。显然,我遗漏了有关 Promise 的一些基本内容——但在阅读了几篇教程和博客文章后,我仍然迷失了方向。

Here's a codepen I wrote up to demonstrate my attempt.

【问题讨论】:

  • promiseReturner(n) 是返回一个承诺还是做一个返回承诺的函数?

标签: javascript promise


【解决方案1】:

您的“非循环”解决方案也不应该起作用。您必须将函数传递给.then,而不是承诺:

var p = Promise.resolve();
for (var i=1; i<=10; i++) {
  (function(i) {
      p = p.then(function() {
          return promiseReturner(i);
      });
  }(i));
}

如果该函数返回一个 Promise,那么您将获得链接效果。

关于MDN的承诺的更多信息。


可以使用let(和箭头函数)进行简化:

var p = Promise.resolve();
for (let i=1; i<=10; i++) {
    p = p.then(() => promiseReturner(i));
}

.bind(即 ES5):

var p = Promise.resolve();
for (var i=1; i<=10; i++) {
    p = p.then(promiseReturner.bind(null, i));
}

【讨论】:

  • 也许更清楚:“你必须传递一个函数返回一个promise”?
  • 我之前曾尝试将它包装在一个函数中 see here,但它似乎只运行了最后一个承诺。为什么将它包装在一个自调用函数中似乎可以解决它?
  • @BenDavis:JavaScript closure inside loops – simple practical example。 tl;dr:JavaScript 只有函数作用域,没有块作用域,所以每个函数都引用相同的 i。这是通过let btw 修复的。
  • 啊,谢谢!我想我自己也很困惑,因为我认为我的 promiseReturner 函数本身就是一个函数,但没有意识到我正在传递该函数的结果(而不是函数本身)。你会认为已经编码 15 年的人会明白这一点。去图吧。
  • @BenDavis: :) 如果promiseReturner 不需要参数,您可以使用p = p.then(promiseReturner);(注意我没有调用该函数)。但实际上,除了 IIFE(立即调用的函数表达式),还有一个替代方案,.bind:p = p.then(promiseReturner.bind(null, i));
【解决方案2】:

如果您使用的是 es6,则可以使用 array.reduce 实现此目的。我觉得很巧妙。

const functions = [/* array of functions which return promises */];
const finalPromise = functions.reduce(async (promise, asyncFn) => {
  await promise;
  return asyncFn();
}, Promise.resolve());

【讨论】:

    【解决方案3】:

    您可以通过 es6 生成器和 co 之类的库来使用 async/await

    co(function* () {
      while(upto < 10) {
        var result = yield Promise.resolve(true);
      }
      return result;
    }).then(function (value) {
      console.log(value);
    }, function (err) {
      console.error(err.stack);
    });
    

    这里有一些细节是如何工作的:http://davidwalsh.name/async-generators

    【讨论】:

      【解决方案4】:

      这是我用来解决相同问题的解决方案:

      var recursiveFunction = function(values) {
        return new Promise(function(resolve, reject) {
          if (values.length <= 0) {
              return resolve();
          } else {
              return promiseReturner(values[0]).then(function() {
                  values.shift();
                  return recursiveFunction(values).then(function() {
                    resolve();
                  });
              });
            }
        });
      }
      
      recursiveFunction([1,2]).then(function(r) {
       console.warn('Finished solving promises sequentially');
      })
      

      【讨论】:

        【解决方案5】:

        相对于彼此同步执行 Promise 可能会很棘手。我在下面包含了一个使用 Axios Promise 的示例,但您可以用自己的 Promise 替换它们。祝你好运!

        const get = (endpoint = '/', params = {}) => {
          // return axios promise
          return axios({
            method: 'get',
            url: apiHost + endpoint,
            headers: { 'Authorization': 'Token ' + this.state.token },
            params: params,
          });
        };
        
        get('/api/some-endpoint/')
          .then((response) => {
            console.log(response);
            //return next promise
            return get('/api/another-endpoint/');
          }).then((response) => {
            console.log(response);
            // return next promise
            return get('/api/yet-endpoint');
          }).then((response) => {
            console.log(response);
            // return next promise
            return get('/api/last-endpoint/');
          }).then((response) => {
            console.log(response);
            // finished, no more promises left in the chain
          })
          .catch(function (error) {
            console.log('Error getting data', error);
          });
        

        【讨论】:

          【解决方案6】:

          你可以通过nsynjs运行你的代码,它会暂停每个返回promise的函数的执行,并等待promise被解决:

          var promiseReturner = function(i) {
              return new Promise(function(resolve, reject) {
                  setTimeout(function(){
                      resolve("result is "+i)
                  }, 1000);
              });
          };
          
          function synchronousCode() {
              for (var i=1; i<=10; i++) {
                  var p=promiseReturner(i); // nsynjs will pause here until promise is resolved
                  console.log(p.data); // `data` will contain result of the promise
              }
          };
          	
          nsynjs.run(synchronousCode, null, function(){
          	console.log("finish");
          });
          &lt;script src="https://rawgit.com/amaksr/nsynjs/master/nsynjs.js"&gt;&lt;/script&gt;

          【讨论】:

          • 看了npm自述页面后,听起来这个库本质上是es6“await”命令的一个过于复杂的自定义实现。如果我错了,请纠正我,但我真的不相信用 javascript 为 javascript 编写自定义评估引擎是个好主意。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2015-12-08
          • 2016-06-13
          • 2015-08-21
          • 2017-02-18
          • 2016-06-29
          • 1970-01-01
          • 2016-04-02
          相关资源
          最近更新 更多