Обещания и почему ваш кодекс их не выполняет

Что такое синтаксис async/await и как его использовать?

Одним из самых первых вещей, которым учат будущих студентов-информатиков, является то, что код будет делать именно то, что ему велят. Студенты сначала пришли в восторг от этого утверждения - отлично, программировать легко! Однако быстро приходит понимание того, что именно код нужно сообщить, и что иногда он может стать довольно сложным.

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

Асинхронный

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

Подождите

Если вы не await завершаете асинхронную функцию, он просто скажет: «Ну, я обещал, что в конце концов что-то вам верну, так что вот [object Promise]». Однако при попытке немедленно изменить возвращаемые значения этих функций лучше всего дождаться выполнения обещаний, прежде чем продолжить. И тогда мы используем async/await.

Давайте подробнее рассмотрим, как мы можем использовать этот специальный синтаксис, чтобы гарантировать, что наш код работает гладко и, как ожидалось.

Внутри функции

Ниже я инициализировал асинхронную функцию под названием createSheet(), внутри которой я использую Google Sheets API, чтобы создать электронную таблицу и вернуть ее URL. Вот что я нашел для успеха:

async function createSheet() {
    let response = await sheet.spreadsheets.create(request);
    return response.data.spreadsheetUrl;
}

Обратите внимание на использование ключевого слова await. Без него наш код Node.js не знает, что ему нужно дождаться завершения вызова API - для создания нашей электронной таблицы - чтобы продолжить.

API Sheets указывает, что метод spreadsheets.create возвращает вновь созданный экземпляр Spreadsheet, из которого мы можем извлечь определенные поля, такие как его идентификатор или URL. Если бы мы не использовали await , переменной ответа было бы [object Promise], и мы бы пытались получить доступ к URL-адресу листа до того, как сам лист был определен… не идеально.

Вызов функции

Поскольку createSheet() определяется как async, вызов с использованием ключевого слова await означает, что мы хотим, чтобы наш код дождался полного завершения выполнения createSheet(), прежде чем переходить к другому коду.

return await createSheet(/**parameters**/);

Подумайте об этом так: мы ждем ответа от нашей async функции createSheet(). Эта функция знает, что мы ее ждем, поэтому обещает, что в конечном итоге она вернет значение. Терпение - добродетель!

Без ожидания мы вместо этого вернем [object Promise]. Использование async/await гарантирует, что мы получаем точное возвращаемое значение, которое ожидаем от функции, и не продвигаемся вперед в нашем коде до завершения выполнения определенных задач.

Нарушение потока

Вот явный пример того, как без использования await ваш код не знает, когда чего ждать:

var url = ‘’;
let response = createSheet(/**parameters**/).then(res => {
  url = res.data.spreadsheetUrl;
  console.log('one');
});
console.log('two');
return url;

Если вы заглянете в журнал консоли, то заметите, что на самом деле «два» было напечатано перед «одним»… хммм….

Это потому, что без использования async / await ваш вызывающий код становится нетерпеливым и переходит к оператору return до того, как createSheet() получит шанс выполнить свое обещание. Итак, ваш возвращенный URL-адрес будет просто пустой строкой. Грустный.

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

По мере практики синтаксис async/await быстро становится второй натурой и может показаться довольно простым. Надеюсь, этот пост поможет сэкономить пару часов бездумной отладки.

Вы можете узнать больше о async/await и обещаниях, прочитав его документацию или поэкспериментировав с написанием облачной функции Google в Node.js.

Это правда, что код будет делать именно то, что вы ему прикажете. Верно и то, что иногда коду нужно давать дополнительные инструкции, чтобы не потерять терпение. Если бы только для людей существовал async/await эквивалент ...

Вы можете найти весь использованный здесь код в этом сообщении блога на моем личном GitHub.