【问题标题】:How to use loops in promises如何在 Promise 中使用循环
【发布时间】: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。我还建议您多阅读一些有关承诺的内容,您的代码真的很混乱。

标签: node.js reactjs promise


【解决方案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();
      }
      

      请记住,所有承诺都必须解决或拒绝。如果你不遵守这条规则,你就会遇到麻烦。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-01-07
        • 2021-05-11
        • 2020-07-27
        • 2019-02-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多