Использование для ожидания ... с синхронными итерациями

11

MDN говорит, что for await...of есть два варианта использования:

for await...ofОператор создает петлю итерации асинхронной итерации объектов, а также на итерируемых синхронизации, ...

Ранее я знал о первом: использование асинхронных итераций Symbol.asyncIterator. Но сейчас меня интересует последнее: синхронные итерации.

Следующий код выполняет итерацию по синхронной итерации - массиву обещаний. Похоже, блокирует прогресс на выполнение каждого обещания.

async function asyncFunction() {
    try {
        const happy = new Promise((resolve)=>setTimeout(()=>resolve('happy'), 1000))
        const sad = new Promise((_,reject)=>setTimeout(()=>reject('sad')))
        const promises = [happy, sad]
        for await(const item of promises) {
            console.log(item)
        }
    } catch (err) {
        console.log(`an error occurred:`, err)
    }
}

asyncFunction() // "happy, an error occurred: sad" (printed in quick succession, after about 5 seconds)

Поведение похоже на ожидание каждого обещания по очереди, согласно логике, показанной ниже. Это утверждение верно?

Я спрашиваю , потому что этот образец коды имеет проводную до ловушки неявного отказа , что Promise.allи Promise.allSettledизбежать, и это мне кажется странным , что эта модель будет явно поддерживается языком.

Бен Астон
источник
2
Что именно ваш вопрос? Похоже, что приведенные вами примеры работают
Саги Рика
for await... ofВерно ли мое описание с синхронными итерациями, и если да, то имеет ли значение, что этот шаблон может генерировать необработанные ошибки отклонения?
Бен Астон
«Это правильно» - это не вопрос. «Правильно» - это то, что вы говорите.
Роберт Харви
Можете ли вы продемонстрировать с помощью кода возникновение необработанных ошибок отклонения, которые вы описали?
Роберт Харви
Окончательный код демонстрирует это. В этом контексте значение «Правильный» имеет четко определенное значение, поскольку я предоставил код для описания того, что, по моему мнению, он делает. Если поведение соответствует моему коду, то мой код верен, иначе моё понимание неверно. Также наблюдение «Правильно» - это то, что вы говорите. это явно не соответствует действительности. Правильный имеет четко определенное значение в этом контексте.
Бен Астон

Ответы:

4

Да, это странно, и вы не должны этого делать. Не повторяйте массивы обещаний, это в точности приводит к проблеме необработанных отклонений, о которой вы упоминали .

Так почему это поддерживается на языке? Для продолжения небрежно обещаю семантику.

Вы можете найти точное обоснование в этом комментарии к проблеме, обсуждающей эту часть предложения :

Я думаю, что мы должны вернуться к этому, Symbol.iteratorпотому что наша текущая семантика Promise предназначена для того, чтобы позволить вещам синхронизации использоваться как асинхронные вещи. Вы могли бы назвать это "разгильдяйством". Это соответствует логике @ грунтовых вод, описанной выше , но я просто хочу объяснить параллели более подробно.

«Цепная» семантика - .thenэто все об этом. Вы можете вернуть обещание .thenили скалярное значение; все то же самое. Вы призываете Promise.resolveне оборачивать что-либо в Обещание, а приводить что-то к Обещанию - получить асинхронное значение, когда у вас есть что-то или другое.

Семантика asyncи awaitвсе о том, чтобы быть небрежным, а также. Вы можете добавить awaitлюбое не-Promise выражение в асинхронную функцию, и все работает отлично, точно так же, за исключением того, что вы передаете управление очереди заданий. Точно так же, вы можете «оборонительно» расставить async все, что захотите, до тех пор, пока вы получите awaitрезультат. Если у вас есть функция, которая возвращает обещание - что угодно! вы можете сделать это asyncфункцией, и с точки зрения пользователя ничего не изменится (даже если технически вы получите другой объект Promise).

Асинхронные итераторы и генераторы должны работать одинаково. Точно так же, как вы можете ожидать значения, которое, случайно, не было Обещанием, разумный пользователь мог бы ожидать, что сможет yield*синхронизировать итератор в асинхронном генераторе. for awaitЦиклы также должны «просто работать», если пользователь защищенно помечает цикл таким образом, думая, что он может получить асинхронный итератор.

Я думаю, что было бы важно сломать все эти параллели. Это сделало бы асинхронные итераторы менее эргономичными. Давайте обсудим это в следующий раз, когда асинхронные генераторы / итераторы появятся в повестке дня TC39.

Берги
источник
Спасибо. Событие испускается или это какая-то другая ошибка? Я спрашиваю, потому что я думал, что события были частью WebAPI. Используется ли аналогичная передача событий в других частях спецификации?
Бен Астон
@ 52d6c6af Вы имеете в виду unhandledrejectionсобытия?
Берги
Да. Просто для того, чтобы перехватить «ошибку», которую я использовал window.addEventListener('unhandledrejection',...Короче: это единственный пример, который я могу вспомнить, такого рода ошибки, генерируемые JavaScript. Однако я почти наверняка ошибаюсь, думая об этом. И наконец: имеет ли значение эта «ошибка» когда-либо, кроме нежелательного сообщения об ошибке в консоли?
Бен Астон
1
@ 52d6c6af Смотрите здесь и там, как это указано в совместных усилиях между спецификациями ECMAScript и Web API. Нет, событие на самом деле не имеет значения, уже слишком поздно, когда вы получили это. Afaics, он используется только для мониторинга ошибок на стороне клиента.
Берги
Если это на самом деле не имеет значения, является ли совет все же «не повторять массивы обещаний», или это скорее «знать, что это не проявляет безотказного поведения при некоторых обстоятельствах»?
Бен Астон
0

sadОбещание не быть под awaitред когда он терпит неудачу - что потребности кода , чтобы закончить ожидание , happyпрежде чем он может начать ждать sad. sadОбещание не удается , прежде чем happyрешает. ( Promise.allинструмент лучше подходит для этого варианта использования)

Гирсам
источник
1
Я знаю. Отсюда и мой вопрос. Если Promise.allэто лучшее решение, почему язык учитывает этот синтаксис? for await...ofмог бы быть легко реализован для простого перечисления асинхронных итераций. Но они помогли перечислить синхронные итерации (но с (кажущимся?) Ловушкой). Почему?
Бен Астон
1
Ах, я неправильно понял. Мы спрашиваем, почему for await ... ofпринимает синхронные итерации? Я хотел бы поддержать асинхронные генераторы, которые условно могут возвращать синхронные элементы.
Гершом
Да, особенно с учетом того, что, по-видимому, вводится ошибка при отбраковке.
Бен Астон
На мой взгляд, ловушка заключается в создании обещания, а не в ожидании его немедленно. К сожалению, эта ловушка также часто является очень полезной функцией.
Гершом