无法承诺要依序评估

时间:2019-04-07 10:14:49

标签: javascript asynchronous promise es6-promise

我有一个购物车系统,其中请求的顺序非常重要。该系统必须能够创建用户,然后才能向其用户帐户添加项目。为此,我使用了诺言,并试图将它们链接起来,以便它们一个接一个地出现。也许我的理解是有缺陷的,但是我没有使它起作用。

processUsers函数。返回承诺。此功能的顺序也很重要,因为我们需要按顺序创建所有用户帐户,然后才能将它们连接在一起(此系统中的孩子和父母之间存在链接)

this.createUsers和this.createUserRelations都是返回承诺列表的函数

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

更新:事实证明我确实误解了Promises的工作方式。在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)
        })
    }

现在可以使用了!

1 个答案:

答案 0 :(得分:2)

  

我知道Promise.all()不会按顺序运行Promise,但是我认为它将开始运行userPromises,一旦解决它们,它将运行itemPromises。

不,诺言不会“运行”,Promise.all不会“开始”任何事情。一个承诺只是您可以等待的事情,Promise.all将这些事情中的多个因素组合成一个可以等待的承诺。

当您致电processItem()时,这项工作正在开始,您确实会立即致电。如果您在then回调中进行调用,它将在开始处理项目之前等待userPromises

顺便说一句,也请避免使用Promise constructor antipattern

processTicket(ticket) {
    var ticketUser = {}
    const userPromises = ticket.filter((t) =>
        t.item.item_type === ITEM_TYPE_TICKET
    ).map((m) => {
        ticketUser = m.user
        return this.processUsers(m.user, m.parents)
    })
    return Promise.all(userPromises).then(() => {
//  ^^^^^^
        const itemPromises = ticket.map((t) => {
            if(t.item.item_type === ITEM_TYPE_BUS) {
                t.user = ticketUser
            }
            return this.processItem(t)
        })
        return Promise.all(itemPromises)
//      ^^^^^^
    })
}