正确的方式来编写承诺的循环。

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

db.getUser(email).then(function(res) { logger.log(res); }); // this is a promise 

我尝试了以下方法(方法从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'); }); 

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

有什么build议么?

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

其实呢。 该语句在resolve调用之前执行。

有什么build议么?

许多。 最重要的是你使用create-promise-manually反模式 – 只做

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

其次,function可以简化很多:

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

第三,我不会使用while循环(使用闭包variables),而是使用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')); 

如果你真的想要一个通用的promiseWhen()函数来实现这个目的和其他目的的话,那就千方百计吧,用Bergi的简化。 然而,由于承诺的工作方式,通过这种方式传递callback通常是不必要的,并迫使你跳过复杂的小圈子。

据我可以告诉你正在尝试:

  • asynchronous获取一系列电子邮件地址的用户详细信息(至less,这是唯一有意义的场景)。
  • 通过recursion构build.then()链来实现。
  • 在处理返回的结果时保持原来的顺序。

因此,这个问题实际上就是Promise Anti- Patterns中的“The Collection Kerfuffle”中讨论的问题,它提供了两个简单的解决scheme:

  • 使用Array.prototype.map()并行asynchronous调用
  • 使用Array.prototype.reduce()串行asynchronous调用。

平行的方法将(直截了当地)给你想要避免的问题 – 答复的顺序是不确定的。 串行方法将build立所需的.then()链 – 平 – 没有recursion。

 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()); } 

请拨打以下电话:

 //Compose here, by whatever means, an array of email addresses. var arrayOfEmailAddys = [...]; fetchUserDetails(arrayOfEmailAddys).then(function() { console.log('all done'); }); 

正如你所看到的,不需要丑陋的外部variables或它的相关condition函数。 限制(问题中的10)完全由arrayOfEmailAddys的长度arrayOfEmailAddys

以下是我如何使用标准的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) 

特定

  • asyncFn函数
  • 项目数组

需要

  • 承诺链接。然后()的串联(按顺序)
  • 本地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()) 

Bergibuild议的function非常好:

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

我仍然想做一个小小的补充,这在使用承诺时很有意义:

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

这样,while循环就可以embedded到promise链中,并用lastValue来parsing(如果action()永远不会运行的话)。 看例子:

 var count = 10; util.promiseWhile( function condition() { return count > 0; }, function action() { return new Promise(function(resolve, reject) { count = count - 1; resolve(count) }) }, count) 

我会做这样的事情:

 var request = [] while(count<10){ request.push(db.getUser(email).then(function(res) { return res; })); count++ }; Promise.all(request).then((dataAll)=>{ for (var i = 0; i < dataAll.length; i++) { logger.log(dataAll[i]); } }); 

通过这种方式,dataAll是要logging的所有元素的有序数组。 当所有的承诺完成后,日志操作将会执行。

 function promiseLoop(promiseFunc, paramsGetter, conditionChecker, eachFunc, delay) { function callNext() { return promiseFunc.apply(null, paramsGetter()) .then(eachFunc) } function loop(promise, fn) { if (delay) { return new Promise(function(resolve) { setTimeout(function() { resolve(); }, delay); }) .then(function() { return promise .then(fn) .then(function(condition) { if (!condition) { return true; } return loop(callNext(), fn) }) }); } return promise .then(fn) .then(function(condition) { if (!condition) { return true; } return loop(callNext(), fn) }) } return loop(callNext(), conditionChecker); } function makeRequest(param) { return new Promise(function(resolve, reject) { var req = https.request(function(res) { var data = ''; res.on('data', function (chunk) { data += chunk; }); res.on('end', function () { resolve(data); }); }); req.on('error', function(e) { reject(e); }); req.write(param); req.end(); }) } function getSomething() { var param = 0; var limit = 10; var results = []; function paramGetter() { return [param]; } function conditionChecker() { return param <= limit; } function callback(result) { results.push(result); param++; } return promiseLoop(makeRequest, paramGetter, conditionChecker, callback) .then(function() { return results; }); } getSomething().then(function(res) { console.log('results', res); }).catch(function(err) { console.log('some error along the way', err); }); 

这个使用BlueBird的怎么样 ?

 function fetchUserDetails(arr) { return Promise.each(arr, function(email) { return db.getUser(email).done(function(res) { logger.log(res); }); }); } 

这里是另一种方法(ES6 w / std Promise)。 使用lodash /下划线types退出条件(返回=== false)。 请注意,您可以在选项中轻松添加exitIf()方法以在doOne()中运行。

 const whilePromise = (fnReturningPromise,options = {}) => { // loop until fnReturningPromise() === false // options.delay - setTimeout ms (set to 0 for 1 tick to make non-blocking) return new Promise((resolve,reject) => { const doOne = () => { fnReturningPromise() .then((...args) => { if (args.length && args[0] === false) { resolve(...args); } else { iterate(); } }) }; const iterate = () => { if (options.delay !== undefined) { setTimeout(doOne,options.delay); } else { doOne(); } } Promise.resolve() .then(iterate) .catch(reject) }) }; 

有一个新的方法来解决这个问题,它是通过使用asynchronous/等待。

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

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