Асинхронные функции: делаем обещания дружелюбными

Асинхронные функции позволяют писать код на основе обещаний, как если бы он был синхронным.

Джейк Арчибальд
Jake Archibald

Асинхронные функции включены по умолчанию в Chrome, Edge, Firefox и Safari, и они, откровенно говоря, великолепны. Они позволяют писать код на основе промисов так, как если бы он был синхронным, но без блокировки основного потока. Они делают ваш асинхронный код менее «умным» и более читабельным.

Асинхронные функции работают следующим образом:

async function myFirstAsyncFunction() {
  try {
    const fulfilledValue = await promise;
  } catch (rejectedValue) {
    // …
  }
}

Если вы используете ключевое слово async перед определением функции, вы можете использовать await внутри функции. Когда вы await обещания, функция приостанавливается неблокирующим способом до тех пор, пока обещание не будет выполнено. Если обещание будет выполнено, вы получите стоимость обратно. Если обещание отклоняется, выбрасывается отклоненное значение.

Поддержка браузера

Browser Support

  • Хром: 55.
  • Край: 15.
  • Фаерфокс: 52.
  • Сафари: 10.1.

Source

Пример: регистрация выборки

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

function logFetch(url) {
  return fetch(url)
    .then((response) => response.text())
    .then((text) => {
      console.log(text);
    })
    .catch((err) => {
      console.error('fetch failed', err);
    });
}

И вот то же самое с использованием асинхронных функций:

async function logFetch(url) {
  try {
    const response = await fetch(url);
    console.log(await response.text());
  } catch (err) {
    console.log('fetch failed', err);
  }
}

Это то же количество строк, но все обратные вызовы исчезли. Это облегчает чтение, особенно для тех, кто менее знаком с обещаниями.

Асинхронные возвращаемые значения

Асинхронные функции всегда возвращают обещание, независимо от того, используете ли вы await или нет. Это обещание разрешается с учетом того, что возвращает асинхронная функция, или отклоняется с тем, что выдает асинхронная функция. Итак, с:

// wait ms milliseconds
function wait(ms) {
  return new Promise((r) => setTimeout(r, ms));
}

async function hello() {
  await wait(500);
  return 'world';
}

…вызов hello() возвращает обещание, которое выполняется с помощью "world" .

async function foo() {
  await wait(500);
  throw Error('bar');
}

…вызов foo() возвращает обещание, которое отклоняется с помощью Error('bar') .

Пример: потоковая передача ответа

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

Вот оно с обещаниями:

function getResponseSize(url) {
  return fetch(url).then((response) => {
    const reader = response.body.getReader();
    let total = 0;

    return reader.read().then(function processResult(result) {
      if (result.done) return total;

      const value = result.value;
      total += value.length;
      console.log('Received chunk', value);

      return reader.read().then(processResult);
    });
  });
}

Взгляни на меня, Джейк «обещающий» Арчибальд. Видите, как я вызываю processResult() внутри себя, чтобы настроить асинхронный цикл? Написав это, я почувствовал себя очень умным . Но, как и в случае с большинством «умных» кодов, вам придется смотреть на него целую вечность, чтобы понять, что он делает, как на одну из тех картинок волшебного глаза из 90-х.

Давайте попробуем это еще раз с асинхронными функциями:

async function getResponseSize(url) {
  const response = await fetch(url);
  const reader = response.body.getReader();
  let result = await reader