Пройдя через Asyncrony в моей прошлой статье, я хочу пройти через Async и Await.

Основы асинхронного/ожидания

Есть две части использования async/await в вашем коде.

Асинхронное ключевое слово

Во-первых, у нас есть ключевое слово async, которое вы ставите перед объявлением функции, чтобы превратить ее в асинхронную функцию. Асинхронная функция — это функция, которая знает, как ожидать возможности использования ключевого слова await для вызова асинхронного кода.

Попробуйте ввести следующие строки в JS-консоль вашего браузера:

function hello() { return "Hello" };
hello();

Функция возвращает «Привет» — ничего особенного, верно?

Но что, если мы превратим это в асинхронную функцию? Попробуйте следующее:

async function hello() { return "Hello" };
hello();

Ах. Вызов функции теперь возвращает обещание. Это одна из особенностей асинхронных функций — их возвращаемые значения гарантированно преобразуются в промисы.

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

let hello = async function() { return "Hello" };
hello();

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

let hello = async () => { return "Hello" };

Все они делают в основном одно и то же.

Чтобы фактически использовать значение, возвращаемое при выполнении обещания, поскольку оно возвращает обещание, мы могли бы использовать блок .then():

hello().then((value) => console.log(value))

или даже просто сокращение, такое как

hello().then(console.log)

Как мы видели в прошлой статье.

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

Ключевое слово ожидания

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

Вы можете использовать await при вызове любой функции, которая возвращает обещание, включая функции веб-API.

Вот тривиальный пример:

async function hello() {
  return greeting = await Promise.resolve("Hello");
};
hello().then(alert);

Конечно, приведенный выше пример не очень полезен, хотя и служит для иллюстрации синтаксиса. Давайте продолжим и посмотрим на реальный пример.

Переписывание кода обещания с помощью async/await

Давайте вернемся к простому примеру выборки:

fetch('coffee.jpg')
.then(response => {
  if (!response.ok) {
    throw new Error(`HTTP error! status: ${response.status}`);
  } else {
    return response.blob();
  }
})
.then(myBlob => {
  let objectURL = URL.createObjectURL(myBlob);
  let image = document.createElement('img');
  image.src = objectURL;
  document.body.appendChild(image);
})
.catch(e => {
  console.log('There has been a problem with your fetch operation: ' + e.message);
});

К этому моменту у вас должно быть достаточное представление о промисах и о том, как они работают, но давайте преобразуем это, чтобы использовать async/await, чтобы увидеть, насколько это упрощает работу:

async function myFetch() {
  let response = await fetch('coffee.jpg');
  if (!response.ok) {
    throw new Error(`HTTP error! status: ${response.status}`);
  } else {
    let myBlob = await response.blob();
    let objectURL = URL.createObjectURL(myBlob);
    let image = document.createElement('img');
    image.src = objectURL;
    document.body.appendChild(image);
  }
}
myFetch()
.catch(e => {
  console.log('There has been a problem with your fetch operation: ' + e.message);
});

Это делает код намного проще и понятнее — больше никаких .then() блоков везде!

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

async function myFetch() {
  let response = await fetch('coffee.jpg');
  if (!response.ok) {
    throw new Error(`HTTP error! status: ${response.status}`);
  } else {
    return await response.blob();
  }
}
myFetch().then((blob) => {
  let objectURL = URL.createObjectURL(blob);
  let image = document.createElement('img');
  image.src = objectURL;
  document.body.appendChild(image);
}).catch(e => console.log(e));

Но как это работает?

Вы заметите, что мы обернули код внутри функции и включили ключевое слово async перед ключевым словом function. Это необходимо — вам нужно создать асинхронную функцию, чтобы определить блок кода, в котором вы будете запускать свой асинхронный код; как мы уже говорили ранее, await работает только внутри асинхронных функций.

Внутри определения функции myFetch() вы можете видеть, что код очень похож на предыдущую версию промиса, но есть некоторые отличия. Вместо того, чтобы привязывать блок .then() к концу каждого метода на основе обещаний, вам просто нужно добавить ключевое слово await перед вызовом метода, а затем присвоить результат переменной. Ключевое слово await заставляет среду выполнения JavaScript приостанавливать ваш код в этой строке, не позволяя выполнять дальнейший код до тех пор, пока вызов асинхронной функции не вернет свой результат — очень полезно, если последующий код зависит от этого результата!

После этого ваш код продолжает выполняться, начиная со следующей строки. Например:

let response = await fetch('coffee.jpg');

Ответ, возвращенный выполненным обещанием fetch(), присваивается переменной response, когда этот ответ становится доступным, и синтаксический анализатор приостанавливается в этой строке до тех пор, пока это не произойдет. Как только ответ доступен, синтаксический анализатор переходит к следующей строке, которая создает из нее Blob. Эта строка также вызывает асинхронный метод на основе промисов, поэтому здесь мы также используем await. Когда результат операции возвращается, мы возвращаем его из функции myFetch().

Это означает, что когда мы вызываем функцию myFetch(), она возвращает обещание, поэтому мы можем связать .then() с ее концом, внутри которого мы обрабатываем отображение BLOB-объекта на экране.

Вы, наверное, уже думаете: «Это действительно круто!», и вы правы — меньше .then() блоков для обертывания кода, и это в основном просто выглядит как синхронный код, поэтому он действительно интуитивно понятен.

Еще многое предстоит обсудить. Но это много для одного выстрела. Надеюсь тебе понравилось!