Не удается получить 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() не запускает обещания в последовательном порядке, но я думал, что он начнет выполнять пользовательские промисы, и как только они будут разрешены, он запустит 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)
    }

ОБНОВЛЕНИЕ: Оказывается, я действительно неправильно понимал, как работают промисы. При отображении списка (два раза) в 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); если вы возвращаете обещание из обратного вызова, оно становится частью цепочки.

jonrsharpe 07.04.2019 12:20

@jonrsharpe да, я так и думал. Однако порядок функции processUsers не работает, если я это сделаю. Обещание.все(что-то).затем(Обещание.все(что-тоЕще)). Нужно ли мне сначала передать анонимную функцию, как вы? Promise.all(что-то).then(() => Promise.all()).catch(отклонить)

Donna 07.04.2019 12:24

Да, иначе это не сработает, так как .then ожидает обратный вызов, а не обещание (этот обратный вызов точно может вернуть обещание)

Jonas Wilms 07.04.2019 12:25

В вашем примере вы передаете второй promise.all как функция обратного вызова первому, что не имеет смысла; это должна быть вернулся из функция обратного вызова, как анонимная функция стрелки в моем примере.

jonrsharpe 07.04.2019 12:25

Ааа ок конечно. Виноват. Большое спасибо, что указали на это!

Donna 07.04.2019 12:26
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
5
34
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

I know that Promise.all() doesn't run promises in serial but I thought it would start running userPromises and once they are resolved it would run itemPromises.

Нет, обещания не "запускаются", Promise.all ничего не "запускает". Обещание — это то, чего вы можете дождаться, и Promise.all объединяет несколько этих вещей в одно обещание, которого вы можете дождаться.

Работа начинается, когда вы звоните processItem(), и вы звоните сразу же. Если вы выполняете вызовы внутри обратного вызова then, он будет ждать userPromises, прежде чем начнет обрабатывать элементы.

Кстати, также избегайте Promise конструктор антипаттерн:

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

абсолютно вы правы. Я неправильно понял, я думал, что сам Promise не будет вызван, пока я не назову «тогда» или пока я не использую его в Promise.all, но processItem запускается, как только я его вызываю.

Donna 07.04.2019 14:17

Другие вопросы по теме