如何在 Promise 中使用循环
Posted
技术标签:
【中文标题】如何在 Promise 中使用循环【英文标题】:How to use loops in promises 【发布时间】:2018-06-08 12:17:42 【问题描述】:我试图在 promise 中执行一个 for 循环但没有成功,我认为我的问题与调用 resolve 的位置有关,但我不确定
/*
* Get conversations of user
* @param user String
*/
function getConversations(user)
return new Promise(function(resolve, reject)
var conversations = user.Conversations
var newConversations = []
for(var conversation of conversations)
helperGetConvo(conversation.ConversID).then(function(convo)
newConversations.push(createConversationObject(messages:[], name:convo.conversationName, users:["broulaye", "doumbia"], Id:convo.conversationID))
).catch(function(reason)
console.log("failure when finding conversation 2: " + reason)
)
resolve(newConversations)
)
function helperGetConvo(convoId)
return new Promise (function(resolve, reject)
query.findConversation(convoId).then(function(convers)
if(convers)
console.log("conversation was found: " + convers)
else
console.log("conversation was not found: " + convers)
resolve(convers)
).catch(function(reason)
console.log("failure when finding conversation: " + reason)
)
)
当我像这样执行我的代码时,getConversations 函数只返回一个空数组。但是当我像这样更改 getConversations 函数时:
function getConversations(user)
return new Promise(function(resolve, reject)
var conversations = user.Conversations
var newConversations = []
for(var conversation of conversations)
helperGetConvo(conversation.ConversID).then(function(convo)
newConversations.push(createConversationObject(messages:[], name:convo.conversationName, users:["broulaye", "doumbia"], Id:convo.conversationID))
resolve(newConversations)
).catch(function(reason)
console.log("failure when finding conversation 2: " + reason)
)
)
我确实得到了一个输出,但是我相信它并没有通过整个 forloop,因为据我了解,解析工作就像一个返回语句。
请大家帮忙
【问题讨论】:
你必须等待 Promise 解决,看看 Promise.all。我还建议您多阅读一些有关承诺的内容,您的代码真的很混乱。 避免promise constructor anti-pattern 【参考方案1】:问题在于,当您调用resolve
时,您正在解决整个承诺。 for 循环不会等待每个helperGetConvo()
调用完成,然后再继续下一个。无论这些承诺中的哪一个首先遇到then
语句,都会调用resolve
,这就是你的外部承诺将解决的问题。
你可以阅读更多关于 Promise 的内容:Understanding promises in node.js。
如果您想等待一组 Promise 完成,请使用 Promise.all
。它接受一个 Promise 列表,并且只有在所有 Promise 都成功完成时才会解决。
function getConversations(user)
return new Promise(function (resolve, reject)
var conversations = user.Conversations;
var newConversations = [];
//create a list of promises
var promises = [];
for (var conversation of conversations)
// push each promise into our array
promises.push(
helperGetConvo(conversation.ConversID).then(function (convo)
newConversations.push(createConversationObject(
messages: [],
name: convo.conversationName,
users: ['broulaye', 'doumbia'],
Id: convo.conversationID
));
).catch(function (reason)
console.log('failure when finding conversation 2: ' + reason);
)
);
// wait for all promises to complete
// when then do, resolve the newConversations variable
// which will now have all of the conversation objects that we wanted to create
Promise.all(promises).then(() => resolve(newConversations)).catch(reject);
);
您也可以使用 async/await 来清理它。 Async/await 提供了一些很好的语法糖来消除对return new Promise(...)
的需要。下一个代码 sn-p 不是使用 async/await 的最佳方式,因为 for 循环将同步处理所有内容(一次一个对话)。这篇博文对我理解在迭代问题中使用 async/await 非常有帮助:https://blog.lavrton.com/javascript-loops-how-to-handle-async-await-6252dd3c795。
async function getConversations(user)
var conversations = user.Conversations;
var newConversations = [];
// process each converstaion in sequence
for (var conversation of conversations)
// instead of doing .then() we can use await
// convo will have the result from the helperGetConvo
// we put it in a try/catch because output
// we still want to have the error if something fails
try
var convo = await helperGetConvo(conversation.ConversID);
newConversations.push(createConversationObject(
messages: [],
name: convo.conversationName,
users: ['broulaye', 'doumbia'],
Id: convo.conversationID
));
catch(reason)
console.log('failure when finding conversation 2: ' + reason);
// return
return newConversations;
异步函数返回承诺。所以你可以通过getConversations(user).then(...)
来调用这个函数。但我认为 async/await 让你的代码看起来更干净。您肯定可以进行进一步的优化,但希望这可以帮助您入门。
【讨论】:
我认为你在创造不必要的承诺。 bluebirdjs.com/docs/… 可能。我试图尽可能多地保留 OP 的原始代码,并展示他如何在该结构中利用Promise.all
。如果可以选择,我会将这一切更改为 async/await 以清理它。
@EricGuan 是的,我可能是,我不太熟悉这就是为什么。
@TheF1rstPancake 我不熟悉 async/await 你将如何使用它来清理它?我尝试使用它一次,一切都崩溃了,所以我切换回这个【参考方案2】:
您可以在我试图解决类似问题时发现的辅助函数中循环一个 Promise。我使用这种方法来循环 Promise,因为它不会在第一个被拒绝的 Promise 时摔倒。相反,我可以处理解析或拒绝,并在循环完成后返回最终结果。下面代码sn-p中的Promise使用的是bluebird,http://bluebirdjs.com/docs/getting-started.html
function promiseWhile(condition, action)
return new Promise((resolve, reject) =>
var loop = () =>
if (!condition()) return resolve();
return Promise.cast(action())
.then(loop)
.catch(reject);
;
process.nextTick(loop);
return resolve;
)
我修改了您提供的一些虚拟数据的代码示例,并使其与辅助函数一起使用。因此,我相信您的 getConversations 函数将如下所示:
function getConversations(user)
var conversations = user.Conversations;
var newConversations = [];
var stop = conversations.length;
var index = 0
//loop promise
return promiseWhile(() =>
// Condition for stopping
return index < stop;
, () =>
// Action to run, should return a promise
return new Promise((resolve, reject) =>
helperGetConvo(conversations[index].ConversID)
.then(function(convo)
newConversations.push(createConversationObject(
messages: [],
name: convo.conversationName,
users: ['broulaye', 'doumbia'],
Id: convo.conversationID
));
index++;
resolve();
)
.catch((error) =>
console.log('failure when finding conversation: ' + error);
index++;
resolve();
);
)
)
//This will execute when loop ends
.then(() =>
return newConversations;
);
希望这会有所帮助。
【讨论】:
【参考方案3】:你需要使用Promise.all
function getConversations(user)
var conversations = user.Conversations
var promises = conversations.map(c=>helperGetConvo(c.ConversID))
return Promise.all(promises)
.then(data=>
let newConversations = data.map(convo=>
return createConversationObject(messages:[], name:convo.conversationName, users:["broulaye", "doumbia"], Id:convo.conversationID)
)
return newConversations
)
.catch(reason=>
console.log("failure when finding conversation: " + reason)
)
像这样使用函数
getConversations(user).then(newConversations=>
//your code
)
【讨论】:
【参考方案4】:一种方法是使用 map 而不是 for-in 在数组中收集 Promise。然后使用 Promise.all() 等待它们全部解决(或一个被拒绝)。
类似:
return Promise.all(conversations.map(conversation =>
return helperGetConvo(...).then().catch();
请记住,所有承诺都必须解决或拒绝。如果你不遵守这条规则,你就会遇到麻烦。
【讨论】:
以上是关于如何在 Promise 中使用循环的主要内容,如果未能解决你的问题,请参考以下文章