Автор оригинала: Scott Robinson.
Одна из самых захватывающих функций, приходящих к JavaScript (и, следовательно, Node.js), это async
/ ждать
Синтаксис введен в ES7 Отказ Хотя в основном просто синтаксический сахар на вершине обещаний, эти два ключевых слова должны сделать писать асинхронный код в узле гораздо более терпимым. Все это, но устраняет проблему Обратный вызов Ад И даже давайте будем использовать структуры управления контролем вокруг нашего асинхронного кода.
На протяжении всей этой статьи мы посмотрим, что не так с обещаниями, как новый ждать
Функция может помочь, а как вы можете начать использовать его прямо сейчас Отказ
Проблема с обещаниями
Концепция «обещания» в JavaScript на некоторое время было около некоторое время, и это было полезно в течение многих лет благодаря 3-м партиям библиотек, таких как Bluebird и Q , не говоря уже о недавно добавленной нативной поддержке в ES6.
Они были отличным решением проблемы обратного вызова ада, но, к сожалению, они не решают все асинхронные проблемы. Хотя большое улучшение, обещания оставляют нас, желая еще больше упрощения.
Допустим, вы хотите использовать API для отдыха GitHub, чтобы найти количество звезд, которые есть проект. В этом случае вы, вероятно, использовали великий Запрос-обещание библиотека. Используя подход, основанный на обещании, вы должны сделать запрос и получить результат в обратном вызове, который вы переходите на .then ()
, как это:
var request = require('request-promise'); var options = { url: 'https://api.github.com/repos/scottwrobinson/camo', headers: { 'User-Agent': 'YOUR-GITHUB-USERNAME' } }; request.get(options).then(function(body) { var json = JSON.parse(body); console.log('Camo has', json.stargazers_count, 'stars!'); });
Это распечатает что-то вроде:
$ node index.js Camo has 1,000,000 stars!
Хорошо, может быть, это число слабое преувеличение, но вы получаете точку;)
Делать только один запрос, как это не слишком сложно с обещаниями, но что, если мы хотим сделать тот же запрос на множество различных репозиториев на Github? И что произойдет, если нам нужно добавить контрольный поток (например, условные или петли) по запросу? Поскольку ваши требования становятся более сложными, обещания становятся все труднее работать и все еще в конечном итоге усложняют ваш код. Они все еще лучше, чем обычные обратные вызовы, поскольку у вас нет неограниченного вложения, но они не решают все ваши проблемы.
Для более сложных сценариев, таких как один в следующем коде, вам нужно хорошо восхиться в цепочке обещаний вместе и понимания Когда и где Ваш асинхронный код выполняется.
"use strict"; var request = require('request-promise'); var headers = { 'User-Agent': 'YOUR-GITHUB-USERNAME' }; var repos = [ 'scottwrobinson/camo', 'facebook/react', 'scottwrobinson/twentyjs', 'moment/moment', 'nodejs/node', 'lodash/lodash' ]; var issueTitles = []; var reqs = Promise.resolve(); repos.forEach(function(r) { var options = { url: 'https://api.github.com/repos/' + r, headers: headers }; reqs = reqs.then(function() { return request.get(options); }).then(function(body) { var json = JSON.parse(body); var p = Promise.resolve(); // Only make request if it has open issues if (json.has_issues) { var issuesOptions = { url: 'https://api.github.com/repos/' + r + '/issues', headers: headers }; p = request.get(issuesOptions).then(function(ibody) { var issuesJson = JSON.parse(ibody); if (issuesJson[0]) { issueTitles.push(issuesJson[0].title); } }); } return p; }); }); reqs.then(function() { console.log('Issue titles:'); issueTitles.forEach(function(t) { console.log(t); }); });
Примечание : Github агрессивно-пределы, ограничивающие неаутентические запросы, поэтому не удивляйтесь, если вы обрезаете после запуска вышеуказанного кода всего несколько раз. Вы можете увеличить этот предел по Передача идентификатора клиента/секрет Отказ
На момент написания этого письма выполнение этого кода даст следующее:
$ node index.js Issue titles: feature request: bulk create/save support Made renderIntoDocument tests asynchronous. moment issue template test: robust handling of env for npm-test-install
Просто добавив для
петля и Если
Заявление о нашем асинхронном коде делает гораздо сложнее читать и понимать. Этот вид сложности может быть только в течение длительного времени, прежде чем оно становится слишком сложно работать.
Глядя на код, вы можете сразу сказать мне, где запросы на самом деле выполняются, или в каком порядке работает каждый код кода? Вероятно, не без чтения его тщательно.
Упрощение с async/ждать
Новый async
/ ждать
Синтаксис позволяет все еще использовать обещания, но это устраняет необходимость предоставления обратного вызова к цепочке тогда ()
методы. Значение, которое было бы отправлено на тогда ()
Возможность обратного вызова возвращается непосредственно из асинхронной функции, как если бы это была функция синхронного блокировки.
let value = await myPromisifiedFunction();
Пока, казалось бы, это огромное упрощение к дизайну асинхронного кода JavaScript. Единственный дополнительный синтаксис, необходимый для достижения этого ждать
ключевое слово. Поэтому, если вы понимаете, как обещает работать, то еще не будет слишком сложно понять, как использовать эти новые ключевые слова, поскольку они строят на вершине концепции обещаний. Все, что вам действительно нужно знать, это то, что любое обещание может быть ждать
-Дереный Отказ Значения также могут быть ждать
– как обещание может .resolve ()
на целом или строке.
Давайте сравним метод на основе обещания с ждать
ключевое слово:
Обещания
var request = require('request-promise'); request.get('https://api.github.com/repos/scottwrobinson/camo').then(function(body) { console.log('Body:', body); });
Ждите
var request = require('request-promise'); async function main() { var body = await request.get('https://api.github.com/repos/scottwrobinson/camo'); console.log('Body:', body); } main();
Как вы можете видеть, ждать
Указывает, что вы хотите разрешить обещание и не вернуть этот фактический объект обещания Как это нормально. Когда эта строка выполнена, Запрос
Вызов будет поставлен на стек цикла событий и выполнение, принесет к другому асинхронному коду, который готов к обработке.
async
Ключевое слово используется, когда вы определяете функцию, содержащую асинхронный код. Это показатель, который обещание возвращается из функции и поэтому следует рассматривать как асинхронные.
Вот простой пример его использования (обратите внимание на изменение определения функции):
async function getCamoJson() { var options = { url: 'https://api.github.com/repos/scottwrobinson/camo', headers: { 'User-Agent': 'YOUR-GITHUB-USERNAME' } }; return await request.get(options); } var body = await getCamoJson();
Теперь, когда мы знаем, как использовать async
и ждать
Вместе, давайте посмотрим, что более сложный код основанный на обещании ранее выглядит сейчас:
"use strict"; var request = require('request-promise'); var headers = { 'User-Agent': 'scottwrobinson' }; var repos = [ 'scottwrobinson/camo', 'facebook/react', 'scottwrobinson/twentyjs', 'moment/moment', 'nodejs/node', 'lodash/lodash' ]; var issueTitles = []; async function main() { for (let i = 0; i < repos.length; i++) { let options = { url: 'https://api.github.com/repos/' + repos[i], headers: headers }; let body = await request.get(options); let json = JSON.parse(body); if (json.has_issues) { let issuesOptions = { url: 'https://api.github.com/repos/' + repos[i] + '/issues', headers: headers }; let ibody = await request.get(issuesOptions); let issuesJson = JSON.parse(ibody); if (issuesJson[0]) { issueTitles.push(issuesJson[0].title); } } } console.log('Issue titles:'); issueTitles.forEach(function(t) { console.log(t); }); } main();
Это, безусловно, более читаемо, чтобы он мог быть записан как многие другие линейно-выполненные языки.
Теперь единственная проблема в том, что каждый request.get ()
Вызов выполнен в серии (что означает, что каждый вызов должен подождать, пока предыдущий вызов не закончится перед выполнением), поэтому мы должны дольше ждать дольше для завершения выполнения выполнения, прежде чем получать наши результаты. Лучший вариант будет запущен запросы HTTP GET параллельно. Это все еще можно сделать, используя Обещание. Все ()
Как мы бы сделали раньше. Просто замените для
петля с .map ()
Позвоните и отправьте полученный массив обещаний на Обещание. Все ()
, как это:
// Init code omitted... async function main() { let reqs = repos.map(async function(r) { let options = { url: 'https://api.github.com/repos/' + r, headers: headers }; let body = await request.get(options); let json = JSON.parse(body); if (json.has_issues) { let issuesOptions = { url: 'https://api.github.com/repos/' + r + '/issues', headers: headers }; let ibody = await request.get(issuesOptions); let issuesJson = JSON.parse(ibody); if (issuesJson[0]) { issueTitles.push(issuesJson[0].title); } } }); await Promise.all(reqs); } main();
Таким образом, вы можете воспользоваться скоростью параллельного исполнения и Простота ждать
Отказ
Есть больше преимуществ, чем просто возможность использовать традиционный контроль, такие как петли и условные условные. Этот линейный подход позволяет нам вернуться к использованию попробуй ... поймать
Заявление для обработки ошибок. С обещаниями, которые вы должны были использовать .catch ()
Метод, который работал, но может вызвать путаницу, определяющую, что обещает оно поймали исключения для.
Так что теперь это …
request.get('https://api.github.com/repos/scottwrobinson/camo').then(function(body) { console.log(body); }).catch(function(err) { console.log('Got an error:', err.message); }); // Got an error: 403 - "Request forbidden by administrative rules. Please make sure your request has a User-Agent header..."
… Может быть выражено так:
try { var body = await request.get('https://api.github.com/repos/scottwrobinson/camo'); console.log(body); } catch(err) { console.log('Got an error:', err.message) } // Got an error: 403 - "Request forbidden by administrative rules. Please make sure your request has a User-Agent header..."
Хотя это примерно в том же количестве кода, гораздо проще прочитать и понимать, что кто-то переходит на JavaScript с другого языка.
Используя async прямо сейчас
Особенность ASYNC все еще находится на этапе предложения, но не волнуйтесь, есть еще несколько способов использовать это в вашем коде прямо сейчас Отказ
V8.
Пока он еще не добрался до узла, команда V8 имеет заявил публично их намерение реализовать async
/ ждать
характерная черта. Они даже уже совершили реализацию прототипов выполнения, что означает, что поддержка гармонии не должна быть слишком далеко.
Варить
Возможно, самый популярный вариант – транпиливать свой код, используя Бабел И его различные плагины. Babel чрезвычайно популярен благодаря способности смешивать и соответствовать функциям ES6 и ES7, используя их систему плагинов. Хотя немного сложнее, чтобы настроить, он также обеспечивает гораздо больше контроля для разработчика.
Регенератор
Регенератор Проект Facebook не имеет столько же функций, что и Вавилон, но это более простой способ получить работу Async Proadpiling.
Самая большая проблема, с которой я имел в том, что его ошибки не очень описаны. Поэтому, если в вашем коде имеется синтаксическая ошибка, вы не получите большую помощь от регенератора в нахождении его. Кроме того, я был доволен этим.
Трассер
У меня нет опыта с этим лично, но Traceur (Google) кажется еще одним популярным вариантом с большим количеством доступных функций. Вы можете найти больше информации здесь Подробнее о том, какие функции ES6 и ES7 могут быть переданы.
asyncawait.
Большинство вариантов, доступных для вас, связанные с трансмиляцией, либо использование ночной сборки V8, чтобы получить async
за работой. Другой вариант – использовать Asyncawait Пакет, который предоставляет функцию разрешения обещаний аналогично ждать
характерная черта. Это хороший ванильный способ получения подобного синтаксиса.
Заключение
Вот и все! Лично я больше всего взволнован этой функцией в ES7, но есть другие Отличные функции в ES7 Что вы должны проверить, как класс декораторы и свойства.
Вы используете транспортированный код ES7? Если да, то какая функция была самой выгодной для вашей работы? Дайте нам знать об этом в комментариях!