正确的写入循环的方法.

use*_*480 113 javascript node.js promise bluebird

如何正确构造一个循环以确保后续的promise调用和链式logger.log(res)通过迭代同步运行?(蓝鸟)

db.getUser(email).then(function(res) { logger.log(res); }); // this is a promise
Run Code Online (Sandbox Code Playgroud)

我尝试了以下方法(来自http://blog.victorquinn.com/javascript-promise-while-loop的方法)

var Promise = require('bluebird');

var promiseWhile = function(condition, action) {
    var resolver = Promise.defer();

    var loop = function() {
        if (!condition()) return resolver.resolve();
        return Promise.cast(action())
            .then(loop)
            .catch(resolver.reject);
    };

    process.nextTick(loop);

    return resolver.promise;
});

var count = 0;
promiseWhile(function() {
    return count < 10;
}, function() {
    return new Promise(function(resolve, reject) {
        db.getUser(email)
          .then(function(res) { 
              logger.log(res); 
              count++;
              resolve();
          });
    }); 
}).then(function() {
    console.log('all done');
}); 
Run Code Online (Sandbox Code Playgroud)

虽然它似乎工作,但我不认为它保证调用logger.log(res)的顺序;

有什么建议?

Roa*_*888 132

如果你真的想要promiseWhen()这个和其他目的的一般功能,那么一定要这样做,使用Bergi的简化.但是,由于promises的工作方式,以这种方式传递回调通常是不必要的,并迫使你跳过复杂的小箍.

据我所知,你正在尝试:

  • 异步获取一组电子邮件地址的一系列用户详细信息(至少,这是唯一有意义的方案).
  • .then()通过递归建立链来实现这一目的.
  • 在处理返回的结果时保持原始顺序.

因此,问题实际上是Promise Anti-patterns中 "The Collection Kerfuffle"中讨论的问题,它提供了两个简单的解决方案:

  • 使用并行异步调用 Array.prototype.map()
  • 使用串行异步调用Array.prototype.reduce().

并行方法将(直接)给出您试图避免的问题 - 响应的顺序是不确定的.串行方法将构建所需的.then()链 - 平 - 没有递归.

function fetchUserDetails(arr) {
    return arr.reduce(function(promise, email) {
        return promise.then(function() {
            return db.getUser(email).done(function(res) {
                logger.log(res);
            });
        });
    }, Promise.resolve());
}
Run Code Online (Sandbox Code Playgroud)

请致电如下:

//Compose here, by whatever means, an array of email addresses.
var arrayOfEmailAddys = [...];

fetchUserDetails(arrayOfEmailAddys).then(function() {
    console.log('all done');
});
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,不需要丑陋的外部变量count或它的相关condition功能.限制(问题中的10)完全由数组的长度决定arrayOfEmailAddys.

  • 感觉这应该是选定的答案.优雅且非常可重复使用的方法. (14认同)
  • 谢谢你的回答.这应该是公认的答案. (4认同)

Ber*_*rgi 78

我不认为它保证调用logger.log(res)的顺序;

实际上,确实如此.该声明在resolve通话之前执行.

有什么建议?

许多.最重要的是你使用create-promise-manual反模式 - 只做

promiseWhile(…, function() {
    return db.getUser(email)
             .then(function(res) { 
                 logger.log(res); 
                 count++;
             });
})…
Run Code Online (Sandbox Code Playgroud)

其次,这个while功能可以简化很多:

var promiseWhile = Promise.method(function(condition, action) {
    if (!condition()) return;
    return action().then(promiseWhile.bind(null, condition, action));
});
Run Code Online (Sandbox Code Playgroud)

第三,我不会使用while循环(带闭包变量)而是for循环:

var promiseFor = Promise.method(function(condition, action, value) {
    if (!condition(value)) return value;
    return action(value).then(promiseFor.bind(null, condition, action));
});

promiseFor(function(count) {
    return count < 10;
}, function(count) {
    return db.getUser(email)
             .then(function(res) { 
                 logger.log(res); 
                 return ++count;
             });
}, 0).then(console.log.bind(console, 'all done'));
Run Code Online (Sandbox Code Playgroud)

  • 哎呀.除了`action`在`promiseFor`中将`value`作为其参数.所以不会让我做这么小的编辑.谢谢,它非常有帮助和优雅. (2认同)
  • 好的,我现在看到了.由于`.bind()`混淆了新的`value`,我想我可能会选择将该函数缩写为可读性.对不起,如果我很厚,但如果`promiseFor`和`promiseWhile`不共存,那么如何调用另一个? (2认同)
  • @herve你基本上可以省略它并用`return Promise.resolve(...)`替换`return ...`.如果您需要针对`condition`或`action`的额外保护措施抛出异常(如[`Promise.method`提供它](http://bluebirdjs.com/docs/api/promise.method.html)),请将整个函数体在`return Promise.resolve().then(()=> {...})` (2认同)
  • @herve实际上应该是`Promise.resolve().then(action)....`或`Promise.resolve(action())....`,你不需要包装`then`的返回值 (2认同)

you*_*rth 37

以下是我使用标准Promise对象的方法.

// Given async function sayHi
function sayHi() {
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log('Hi');
      resolve();
    }, 3000);
  });
}

// And an array of async functions to loop through
const asyncArray = [sayHi, sayHi, sayHi];

// We create the start of a promise chain
let chain = Promise.resolve();

// And append each function in the array to the promise chain
for (const func of asyncArray) {
  chain = chain.then(func);
}

// Output:
// Hi
// Hi (After 3 seconds)
// Hi (After 3 more seconds)
Run Code Online (Sandbox Code Playgroud)

  • @khan在chain = chain.then(func)行上,你可以做任何一个:`chain = chain.then(func.bind(null,"...你的params here"));`或`chain = chain.然后(()=> func("你的params here"));` (4认同)
  • 如何以这种方式发送params? (3认同)

小智 9

特定

  • asyncFn函数
  • 一系列的项目

需要

  • 承诺链接.then()串联(按顺序)
  • 原生es6

let asyncFn = (item) => {
  return new Promise((resolve, reject) => {
    setTimeout( () => {console.log(item); resolve(true)}, 1000 )
  })
}

// asyncFn('a')
// .then(()=>{return async('b')})
// .then(()=>{return async('c')})
// .then(()=>{return async('d')})

let a = ['a','b','c','d']

a.reduce((previous, current, index, array) => {
  return previous                                    // initiates the promise chain
  .then(()=>{return asyncFn(array[index])})      //adds .then() promise for each item
}, Promise.resolve())
Run Code Online (Sandbox Code Playgroud)

  • 如果`async`即将成为JavaScript中的保留字,它可能会增加清晰度以重命名该函数. (2认同)
  • 这是正确的方法! (2认同)

小智 5

有一种新方法可以解决这个问题,那就是使用 async/await。

async function myFunction() {
  while(/* my condition */) {
    const res = await db.getUser(email);
    logger.log(res);
  }
}

myFunction().then(() => {
  /* do other stuff */
})
Run Code Online (Sandbox Code Playgroud)

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function https://ponyfoo.com/articles/understanding-javascript-async-await