for await...of

Інструкція for await...of створює цикл, що перебирає як асинхронні ітерабельні об'єкти, так і синхронні ітерабельні об'єкти, в тому числі вбудовані String, Array, подібні до масивів об'єкти (наприклад, arguments чи NodeList), TypedArray, Map, Set, а також визначені користувачем асинхронні/синхронні ітерабельні об'єкти. Вона викликає користувацький хук до ітерацій з командами, що виконуватимуться для значення кожної окремої властивості об'єкта. Як і оператор await, інструкція може використовуватись лише всередині асинхронної функції.

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

Специфікації

Специфікація
ECMAScript (ECMA-262)
The definition of 'ECMAScript Language: The for-in, for-of, and for-await-of Statements' in that specification.

Сумісність з веб-переглядачами

Update compatibility data on GitHub
DesktopMobileServer
ChromeEdgeFirefoxInternet ExplorerOperaSafariAndroid webviewChrome for AndroidFirefox for AndroidOpera for AndroidSafari on iOSSamsung InternetNode.js
for await...ofChrome Full support 63Edge Full support 79Firefox Full support 57IE No support NoOpera Full support 50Safari Full support 11WebView Android Full support 63Chrome Android Full support 63Firefox Android Full support 57Opera Android Full support 46Safari iOS Full support 11Samsung Internet Android Full support 8.0nodejs Full support 10.0.0
Full support 10.0.0
No support 8.10.0 — 10.0.0
Disabled
Disabled From version 8.10.0 until version 10.0.0 (exclusive): this feature is behind the --harmony-async-iteration runtime flag.

Legend

Full support  
Full support
No support  
No support
User must explicitly enable this feature.
User must explicitly enable this feature.

Див. також