【发布时间】:2019-04-07 20:12:44
【问题描述】:
我有一个购物车系统,其中请求的顺序非常重要。系统必须能够在将项目添加到用户帐户之前创建用户。为此,我使用了 Promise,并尝试将它们链接起来,以便它们一个接一个地出现。也许我的理解有缺陷,但我没有让它发挥作用。
processUsers 函数。返回一个承诺。这个函数的顺序也很重要,因为我们需要在开始连接之前按顺序创建所有用户帐户(在这个系统中孩子和父母之间有一个链接)
this.createUsers 和 this.createUserRelations 都是返回 Promises 列表的函数
processUsers(child, parents) {
return new Promise((resolve, reject) => {
Promise.all(
this.createUsers(child, parents)
).then((userResponse) => {
Promise.all(
this.createUserRelations(
child,
parents
)
).then((relationResponse) => {
resolve(relationResponse)
}).catch(reject)
}).catch(reject)
})
}
这行得通。这个顺序是正确的。我正在通过使服务器上的 create_or_update 函数休眠 5 秒来对此进行测试,实际上 createUserRelations 函数正在等待它。
创建用户后,我使用相同的逻辑向每个用户添加项目
/**
* process a single ticket
* @param {Array} ticket
*/
processTicket(ticket) {
var self = this
return new Promise((resolve, reject) => {
var ticketUser = {}
const userPromises = ticket.filter(
(t) => t.item.item_type === ITEM_TYPE_TICKET
).map((m) => {
ticketUser = m.user
return self.processUsers(m.user, m.parents)
})
const itemPromises = ticket.map(
(t) => {
if(t.item.item_type === ITEM_TYPE_BUS) {
t.user = ticketUser
}
return self.processItem(t)
}
)
Promise.all(userPromises).then((data) => {
Promise.all(itemPromises).then((data) => {
resolve(data)
}).catch(reject)
}).catch(reject)
})
}
这不起作用。 itemPromises 不会等待 userPromises 完成,因此我收到一个错误,因为服务器找不到用户来链接该项目。 我知道 Promise.all() 不会串行运行 Promise,但我认为它会开始运行 userPromises,一旦解决,它将运行 itemPromises。它似乎没有这样做。 我尝试了多种其他方法,例如使用 p-queue。
这里是processItem函数
processItem(item) {
// returns a Promise
return users.add_order_item(
this.sessionUser.id,
item.user.email,
item.item.id,
item.delivery
)
}
最后是处理整个订单的主要功能
processOrder() {
const items = this.orderSessionItems
const reduced = this.groupBy(
items, (i) => i.reference_number)
var self = this
const promises = Object.keys(reduced).map((key, index) => {
return self.processTicket(reduced[key])
})
return Promise.all(promises)
}
更新:原来我确实误解了 Promise 的工作原理。在 processTicket 中映射列表(两次)时,会立即调用 processItem 承诺。我认为情况并非如此,但它在我执行 Promise.all() 之前被调用。
我最终得到的是这个
processTicket(ticket) {
return new Promise((resolve, reject) => {
var self = this
var ticketUser = {}
const userPromises = ticket.filter(
(t) => t.item.item_type === ITEM_TYPE_TICKET
).map((m) => {
ticketUser = m.user
return self.processUsers(m.user, m.parents)
})
Promise.all(userPromises).then(() => {
const itemPromises = ticket.map(
(t) => {
if(t.item.item_type === ITEM_TYPE_BUS) {
t.user = ticketUser
}
return self.processItem(t)
}
)
Promise.all(itemPromises)
.then((data) => resolve(data))
.catch(reject)
}).catch(reject)
})
}
现在它可以工作了!
【问题讨论】:
-
你应该很少需要
new这样的承诺:第一个函数应该只是return Promise.all(...).then(() => Promise.all(...)).catch(reject);如果你从回调中返回一个 Promise,它就会成为链的一部分。 -
@jonrsharpe 是的,我就是这么想的。但是,如果我这样做,则 processUsers 函数的顺序不起作用。 Promise.all(something).then(Promise.all(somethingElse))。我需要像你一样先将匿名函数传递给它吗? Promise.all(something).then(() => Promise.all()).catch(reject)
-
是的,否则它将无法工作,因为
.then期望回调,而不是承诺(回调肯定会返回承诺) -
在您的示例中,您将第二个 promise.all 作为回调函数传递给第一个,这没有意义;它需要从回调函数返回,就像我的示例中的匿名箭头函数。
-
嗯,当然可以。我的错。非常感谢您指出这一点!
标签: javascript asynchronous promise es6-promise