Введение
Написание асинхронного кода – задача не из легких. Когда дело доходит до JavaScript, мы в значительной мере полагаемся на функции обратного вызова для выполнения асинхронных задач, которые могут быть недостаточно интуитивными. Это создает некоторый барьер входа для новичков в программировании на JavaScript и вызывает частые проблемы у тех, кто уже пользовался языком некоторое время.
В этой статье мы исследуем, как можно использовать предложение для ECMAScript 2016 (ES7) для усовершенствования опыта асинхронного программирования на JavaScript, чтобы сделать наш код более понятным и простым в написании.
Мир сегодняшнего дня
Давайте начнем с рассмотрения попытки асинхронного программирования. Следующий пример использует библиотеку запросов, чтобы сделать http-запрос к the Ron Swanson Quotes API и выведет ответ на консоли. Чтобы это сработало, нужно вставить следующий файл с именем app.js
и запустить npm install request
для установления зависимости. Если у Вас не установлен Node.js, можете скачать его отсюда.
var request = require('request');
function getQuote() {
var quote;
request('http://ron-swanson-quotes.herokuapp.com/quotes', function (error, response, body) {
quote = body;
});
return quote;
}
function main() {
var quote = getQuote();
console.log(quote);
}
main();
Почему это происходит?
Причина того, что цитата переменной неопределенна, в том, что функция обратной связи не вызывается до тех пор, пока не закончится функция запроса. Но так как функция запроса выполняется асинхронно, JavaScript не ждет завершения. Вместо этого он переходит к следующему оператору, который возвращает неопределенную переменную. Для лучшего объяснения того, как JavaScript работает «под капотом», просмотрите эту интересную беседу Филипа Робертса на JSConf EU.
Синхронный код, как правило, легче понять и написать, потому что все выполняется в том порядке, в котором оно написано. Возвращаемые значения имеют широкое применение и довольно интуитивны на других языках, но, к сожалению, мы не можем использовать их так часто, как мы бы хотели в JavaScript, так как они не работают с его асинхронной природой.
Так зачем идти по пути асинхронного кодирования? Представьте, что сетевые запросы и чтение с диска являются тем, что мы называем операциями ввода/вывода (I/O input/output). В синхронном I/O исполнении программа блокируется и ждет передачи данных для завершения. Если это занимает 60 секунд для того, чтобы обратиться к базе данных для завершения, то программа ждет, ничего не делая, в течение 60 секунд. Однако, во время выполнения асинхронной I/O операции, программа может возобновить нормальное выполнение и иметь дело с результатами I/O операции, когда бы они ни поступили. Поэтому и существуют функции обратного вызова, но с обратными вызовами гораздо труднее работать и понимать при чтении источника приложения.
Утопия
Можем ли мы получить лучшее из обоих миров – асинхронный код, который позволяет нам работать с блокированными операциями, но также простой в чтении и написании, как синхронный. Ответ – да. Благодаря предложению ES7 для асинхронных функций (Async/Await).
Когда функция декларирована как асинхронная, тогда она в состоянии обеспечить выполнение вызываемого кода, пока он ожидает решения promise.
Вы можете заменить код в app.js
следующим. Мы также должны установить Babel для запуска. Сделайте это с помощью npm install babel
. Babel будет преобразовывать наш код ES7 в более работоспособную современную версию.
var request = require('request');
function getQuote() {
var quote;
return new Promise(function(resolve, reject) {
request('http://ron-swanson-quotes.herokuapp.com/quotes', function(error, response, body) {
quote = body;
resolve(quote);
});
});
}
async function main() {
var quote = await getQuote();
console.log(quote);
}
main();
console.log('Ron once said,');
Можно видеть, что мы возвращаем promise, что оборачивает сетевой запрос внутри нашей getQuote
функции. Внутри функции обратного вызова, переданной запросу, мы вызываем функцию решения promise с телом результата.
Выполните следующее, чтобы запустить этот пример.
./node_modules/.bin/babel-node app.js
// Ron once said,
// {"quote":"Breakfast food can serve many purposes."}
Этот код выглядит довольно круто и близок к оригинальной попытке. Он выглядит довольно синхронным, хотя это не так. В случае, если Вы не заметили, «Ron once said»
был напечатан первым, несмотря на то, что вызывается после main
. Это показывает, что мы не блокируем работу, пока ожидаем завершения запроса сети.
Відео курси за схожою тематикою:
Внесение улучшений
Мы можем и дальше улучшить код, добавляя обработку ошибок с блоком try/catch. Если существует ошибка во время запроса, мы можем вызвать функцию отмены promise, которая будет искать ошибки внутри main
. Как возвращаемые значения, блоки try/catch ранее недостаточно использовались, потому как их было трудно правильно использовать с асинхронным кодом.
var request = require('request');
function getQuote() {
return new Promise(function(resolve, reject) {
request('http://ron-swanson-quotes.herokuapp.com/quotes', function(error, response, body) {
if (error) return reject(error);
resolve(body);
});
});
}
async function main() {
try {
var quote = await getQuote();
console.log(quote);
} catch(error) {
console.error(error);
}
}
main();
console.log('Ron once said,');
Выполните этот код, и Вы сможете увидеть найденное исключение через изменение URL-запроса во что-то похожее к http://foo
.
Преимущества
Есть несколько довольно приятных преимуществ, которые могут действительно изменить способ, которым мы писали асинхронный код JavaScript. Возможность написать код, который работает асинхронно, но выглядит синхронным и делает такие программные конструкции, как return и try/catch, проще в использовании, безусловно, поможет сделать язык более доступным.
Лучшей частью является то, что мы можем использовать нашу новую особенность со всем, что возвращает promise. Для примера, возьмем библиотеку Twilio Node.js library. Вы также должны иметь учетную запись Twilio.
Начните с запуска npm install twilio
. Затем добавьте следующее в файл под названием twilio.js
и замените поля в линиях, отмеченные // TODO
Вашими собственными учетными данными и цифрами.
./node_modules/.bin/babel-node twilio.js
var twilio = require('twilio');
var client = twilio('YOUR_TWILIO_ACCOUNT_SID', 'YOUR_TWILIO_AUTH_TOKEN'); // TODO
async function sendTextMessage(to) {
try {
await client.sendMessage({
to: to,
from: 'YOUR_TWILIO_PHONE_NUMBER', // TODO
body: 'Hello, Async/Await!'
});
console.log('Request sent');
} catch(error) {
console.error(error);
}
}
sendTextMessage('YOUR_PHONE_NUMBER'); // TODO
console.log('I print out first to show I am async!');
Безкоштовні вебінари за схожою тематикою:
Так же, как мы показали выше с функцией getQuote, мы отметили sendTextMessage
как async
, что позволяет ему ждать (await)
решения, возвращенного promise с client.sendMessage
.
Подводя итоги
Вы увидели, как можно, используя преимущество предложения функции ES7, улучшить наш опыт написания асинхронного JavaScript.
Мы очень взволнованы предложением Async/Await, что сейчас развивается, но пока мы этого ждем, можно использовать Babel, чтобы получить преимущества уже сегодня, со всем, что возвращает promise. Предложение недавно вышло и нуждается в обратной связи. Можете использовать его с Вашими удивительными вещами, которые Вы можете построить.
Источник: https://www.twilio.com/blog/2015/10/asyncawait-the-hero-javascript-deserved.html
Статті за схожою тематикою