Итак, вы нажали на заголовок, чтобы прочитать этот пост. Я знаю, что к тому времени вы уже являетесь разработчиком JavaScript. Вы, наверное, видели «promise» и «async/await» повсюду в Medium и в блогах, написанных разработчиками, хотя бы немного разбирающимися в этом. Есть большой шанс, что вы использовали Promise все это время с мыслью, что «это просто работает», не зная причины цепочки .then.

В этом посте мы немного познакомимся с историей Promise, реализовав пару вызовов API к Github для получения профиля пользователя и репозиториев.

Если вы являетесь опытным разработчиком в течение многих лет, вы, вероятно, пытались это сделать в какой-то момент своей карьеры:

Получение значения от функции, которая асинхронно вызывает API, — непростая задача. Это значение возвращается при вызове функции выше:

var userInfo = getInfo("gaearon"); 
console.log(userInfo); // returns { user: null, repos: [] }

Из-за асинхронной природы языка JavaScript вызовы API не обрабатываются функцией getUser. На этом этапе, возможно, вы попытаетесь использовать обратный вызов, чтобы присвоить переменную в функции обратного вызова:

let callbackUserInfo; 
callbackGetUser("gaearon", userInfo => {
    callbackUserInfo = userInfo; 
});
console.log("callbackUserInfo", callbackUserInfo); // undefined

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

Если вы спешите заставить этот вызов работать, вы, вероятно, полностью удалите функцию и вместо этого прибегнете к основному вызову:

Добро пожаловать в ад обратного звонка.

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

Используйте цепочку обещаний для последовательных асинхронных вызовов

Несколько лет назад Promise стал стандартом, используемым в API-интерфейсах, предоставляемых крупными разработчиками библиотек, до такой степени, что стало еще труднее найти библиотеки, не возвращающие Promise по умолчанию. Запрашивать пользовательские данные с Github намного проще с помощью octokit rest client.

Аналогичный результат можно получить и при использовании аксиос, если использование API-клиента нецелесообразно по ряду причин:

Ах, это довольно аккуратно!

Создайте функцию обещания

Из снипера выше axios.get — это функция, которая возвращает объект Promise. Объект Promise состоит из свойства с именем then и конструктора обратного вызова, который возвращает resolve и reject .

let foo = new Promise(function(resolve, reject) { resolve('Success!') });

Как правило, каждое определенное обещание должно вызывать как resolve, так и reject. Обычно resolve работает как функция, а reject сообщает вызывающей стороне, что внутри функции что-то не так.

Давайте попробуем заново реализовать функцию getUser. Эта функция делает то же самое, что и функция getUser, которую мы видели ранее, с реализованным Promise:

Теперь мы можем вызвать функцию getUser так же, как мы использовали then ранее:

getUser(username).then(data => { console.log("getUser", data) }); 
// output: getUser Object {user: Object, repos: Array[30]}

Обещание.все

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

Это означает, что мы собираемся разделить оба вызова на разные вызовы Promise.

Это выглядело синтаксически правильно, но теперь оба вызова асинхронны и не контролируются, что может легко затруднить выполнение программы в целом. Вам понадобится менеджер, чтобы объединить оба вызова, чтобы вернуть данные, прежде чем продолжить. Давайте наймем Promise.all для этого.

Promise.all позаботится о завершении каждого асинхронного вызова перед преобразованием всех промисов в один массив, состоящий из разрешенных данных каждого асинхронного вызова в порядке, указанном в массиве dataPromises.

Вывод

Существует глубокое знание асинхронного программирования на JavaScript, которое доступно для чтения в Интернете бесплатно, и этот пост в блоге просто царапает поверхность, рассказывая о практическом применении Promise в наших проектах с использованием Node.js или любых современных инструментов JavaScript, включая интерфейсные фреймворки. и библиотеки. Основываясь на моем личном опыте, важно убедиться, что каждый промис обрабатывается, так как программа может продолжать работать бесконечно, если асинхронные вызовы не обрабатываются должным образом, и это особенно сложно отладить, если у вас есть много асинхронных вызовов, разбросанных по файлов внутри проекта.

Пройдя через улучшения от ада обратных вызовов до использования Promise.all для асинхронного запуска двух вызовов API, надеюсь, это прояснит для вас понимание того, как использовать JavaScript Promise.

Внешнее чтение и ресурсы

  1. https://medium.freecodecamp.org/how-to-make-a-promise-out-of-a-callback-function-in-javascript-d8ec35d1f981
  2. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then
  3. https://eloquentjavascript.net/11_async.html

Первоначально опубликовано на https://waiyanyoon.com 22 мая 2019 г.