--- title: for await...of slug: Web/JavaScript/Reference/Statements/for-await...of tags: - JavaScript - await - Інструкція - асинхронний - перебір translation_of: Web/JavaScript/Reference/Statements/for-await...of ---
Інструкція for await...of
створює цикл, що перебирає як асинхронні ітерабельні об'єкти, так і синхронні ітерабельні об'єкти, в тому числі вбудовані {{jsxref("String")}}, {{jsxref("Array")}}, подібні до масивів об'єкти (наприклад, {{jsxref("Functions/arguments", "arguments")}} чи {{DOMxRef("NodeList")}}), {{jsxref("TypedArray")}}, {{jsxref("Map")}}, {{jsxref("Set")}}, а також визначені користувачем асинхронні/синхронні ітерабельні об'єкти. Вона викликає користувацький хук до ітерацій з командами, що виконуватимуться для значення кожної окремої властивості об'єкта. Як і оператор {{jsxref("Operators/await", "await")}}, інструкція може використовуватись лише всередині {{jsxref("Statements/async_function", "асинхронної функції")}}.
for await...of
не працює з асинхронними ітераторами, які не є асинхронними ітерабельними об'єктами.
for await (variable of iterable) { statement }
variable
variable
. Змінна variable
може бути оголошена через const
, let
або var
.iterable
Ви також можете перебирати об'єкт, який явно реалізує протокол асинхронного ітерабельного об'єкта:
const asyncIterable = { [Symbol.asyncIterator]() { return { i: 0, next() { if (this.i < 3) { return Promise.resolve({ value: this.i++, done: false }); } return Promise.resolve({ done: true }); } }; } }; (async function() { for await (let num of asyncIterable) { console.log(num); } })(); // 0 // 1 // 2
Оскільки значення, що повертають асинхронні генератори, відповідають протоколу асинхронного ітерабельного об'єкта, їх можна перебирати циклом for await...of
.
async function* asyncGenerator() { let i = 0; while (i < 3) { yield i++; } } (async function() { for await (let num of asyncGenerator()) { console.log(num); } })(); // 0 // 1 // 2
Для більш конкретного прикладу перебору асинхронного генератора за допомогою for await...of
, розгляньте перебір даних з API.
Цей приклад спочатку створює асинхронний ітерабельний об'єкт для потоку даних, а далі використовує його, щоб дізнатись розмір відповіді від API.
async function* streamAsyncIterable(stream) { const reader = stream.getReader(); try { while (true) { const { done, value } = await reader.read(); if (done) { return; } yield value; } } finally { reader.releaseLock(); } } // Отримує дані з URL та обчислює розмір відповіді за допомогою // асинхронного генератора. async function getResponseSize(url) { const response = await fetch(url); // Міститиме розмір відповіді, у байтах. let responseSize = 0; // Цикл for-await-of. Асинхронно перебирає кожну частку відповіді. for await (const chunk of streamAsyncIterable(response.body)) { // Збільшує загальну довжину відповіді. responseSize += chunk.length; } console.log(`Розмір відповіді: ${responseSize} байтів`); // очікуваний результат: "Розмір відповіді: 1071472 байтів" return responseSize; } getResponseSize('https://jsonplaceholder.typicode.com/photos');
Цикл for await...of
також споживає синхронні ітерабельні об'єкти та генератори. У цьому випадку він внутрішньо чекає на видані значення перед тим, як присвоювати їх керівній змінній циклу.
function* generator() { yield 0; yield 1; yield Promise.resolve(2); yield Promise.resolve(3); yield 4; } (async function() { for await (let num of generator()) { console.log(num); } })(); // 0 // 1 // 2 // 3 // 4 // порівняйте з циклом for-of: for (let numOrPromise of generator()) { console.log(numOrPromise); } // 0 // 1 // Promise { 2 } // Promise { 3 } // 4
Заувага: остерігайтеся видавати відхилені проміси з синхронного генератора. У цьому випадку for await...of
викидає виняток при споживанні відхиленого проміса та НЕ ВИКЛИКАЄ блоки finally
всередині цього генератора. Це може бути небажаним, якщо вам треба звільнити певні виділені ресурси за допомогою try/finally
.
function* generatorWithRejectedPromises() { try { yield 0; yield 1; yield Promise.resolve(2); yield Promise.reject(3); yield 4; throw 5; } finally { console.log('викликано finally') } } (async function() { try { for await (let num of generatorWithRejectedPromises()) { console.log(num); } } catch (e) { console.log('перехоплено', e) } })(); // 0 // 1 // 2 // перехоплено 3 // порівняйте з циклом for-of: try { for (let numOrPromise of generatorWithRejectedPromises()) { console.log(numOrPromise); } } catch (e) { console.log('перехоплено', e) } // 0 // 1 // Promise { 2 } // Promise { <rejected> 3 } // 4 // перехоплено 5 // викликано finally
Для того, щоб блоки finally
у синхронному генераторі завжди викликались, використовуйте належну форму циклу, for await...of
для асинхронних генераторів та for...of
для синхронних, та чекайте на видані проміси явно всередині циклу.
(async function() { try { for (let numOrPromise of generatorWithRejectedPromises()) { console.log(await numOrPromise); } } catch (e) { console.log('перехоплено', e) } })() // 0 // 1 // 2 // перехоплено 3 // викликано finally
Специфікація |
---|
{{SpecName('ESDraft', '#sec-for-in-and-for-of-statements', 'ECMAScript Language: The for-in, for-of, and for-await-of Statements')}} |
{{Compat("javascript.statements.for_await_of")}}