От автора: функции обратного вызова и promise являются строительными блоками для написания асинхронного кода в JavaScript.
В приложении Angular мы можем использовать Rx.js, чтобы использовать возможности Observables, Subject, BehaviorSubject и т. д. для элегантного написания асинхронного кода. В последней версии ECMA JavaScript начал поддерживать функцию «async-await».
Если вы начинали с C #, вам может быть известна функция «async-await», поддерживаемая в C # версии5.
Async-await
Согласно MDN:
Когда функция async вызывается, она возвращает Promise. Когда функция async возвращает значение, Promise будет разрешен с возвращенным значением. Когда функция async генерирует исключение или какое-либо значение, Promise будет отклонен с введенным значением.
Функция async может содержать выражение await, что приостанавливает выполнение функции и ждет разрешения переданного Promise, а затем возобновляет выполнение функции async и возвращает разрешенное значение.
Проще говоря, вы получите возможность писать асинхронный код синхронно.
Пример 1
Давайте рассмотрим простой пример. Функция, которая возвращает Promise, разрешающееся через две секунды и возвращающее значение, переданное в качестве аргумента.
1 2 3 4 5 6 7 |
resolveAfter2Seconds(x) { return new Promise(resolve => { setTimeout(() => { resolve(x); }, 2000); }); } |
Используя Promise, мы можем получить значение, используя функцию обратного вызова then.
1 2 3 4 5 6 |
getValueWithPromise() { this.resolveAfter2Seconds(20).then(value => { console.log(`promise result: ${value}`); }); console.log('I will not wait until promise is resolved'); } |
В приведенном выше случае console.log() в строке 5 должна быть выполнена перед console.log() в строке 3. Это основная природа Promise. Теперь давайте рассмотрим использование async-await.
1 2 3 4 |
async getValueWithAsync() { const value = <number>await this.resolveAfter2Seconds(20); console.log(`async result: ${value}`); } |
Несколько моментов, на которые следует обратить внимание:
Строка 1 — Функция имеет префикс с ключевым словом «async». Обязательно использовать этот префикс, если ваша функция содержит ключевое слово «await».
Строка 2 — Мы не вызываем функцию обратного вызова «.then()» после функции Promise. Вместо этого мы добавляем префикс вызова функции с ключевым словом «await». Это ключевое слово не разрешает выполнение следующего блока кода. Это означает, что console.log() в строке 3 будет выведено только тогда, когда Promise разрешен в строке 2 точно так же, как синхронный вызов функции.
Поскольку мы используем Typescript, нам нужно ввести возвращаемое значение Promise для конкретного типа, <number> в строке 2.
Пример 2
Попробуем добавить два числа, используя подход, основанный на Promise.
1 2 3 4 5 6 7 8 9 10 |
addWithPromise() { this.resolveAfter2Seconds(20).then(data1 => { let result1 = <number>data1; this.resolveAfter2Seconds(30).then(data2 => { let result2 = <number>data2; this.additionPromiseResult = result1 + result2; console.log(`promise result: ${this.additionPromiseResult}`); }); }); } |
В реальных приложениях очень часто имеет место структура вложенного кода promise-then (ад обратных вызовов). Со второго уровня вложенности у нас есть код выше. Представьте 7–8 уровень вложенности с захваченными переменными и исключениями, если таковые имеются. Страшно не так ли?
Теперь подход async.
1 2 3 4 5 6 |
async addWithAsync() { const result1 = <number>await this.resolveAfter2Seconds(20); const result2 = <number>await this.resolveAfter2Seconds(30); this.additionAsyncResult = result1 + result2; console.log(`async result: ${this.additionAsyncResult}`); } |
Просто сравните простоту кода. Оба подхода дадут нам один и тот же результат, но с точки зрения читабельности кода, обслуживание «async-await» превосходит классический подход «promise-then».
Использование REST API Http
Пока мы обсуждали простые примеры. В приложении Angular мы можем получать данные REST, используя Http (скоро выйдет из употребления) или сервиса HttpClient. По умолчанию методы «get()», «put()», «delete()» и «post()» класса HttpClient возвращают «Observable<T>». Этот набор результатов можно использовать с помощью метода «подписки» или с помощью оператора «toPromise()» из RxJs.
Получение результата HttpClient, используя Observable:
Я видел многих разработчиков Angular, использующих «subscribe» для получения данных HESTP REST, не зная их отличия от «Promise». Метод «subscribe» содержится объектом «Observable». После подписки обратный вызов «subscribe» должен выполняться всякий раз, когда «Observable» создает новые данные. Принимая во внимание, что обработчик обратного вызова Promise «then()» должен быть выполнен максимум один раз. Поэтому до тех пор, пока вам не потребуются повторяющиеся данные, не используйте «subscribe». Вместо этого используйте «toPromise()». Если вы заметили, в примерах, приведенных в официальной документации Angular, часто используется «toPromise».
1 2 3 4 5 6 7 |
getDataUsingSubscribe() { this.httpClient.get<Employee>(this.url).subscribe(data => { this.subscribeResult = data; console.log('Subscribe executed.') }); console.log('I will not wait until subscribe is executed..'); } |
Получение результата HttpClient, используя toPromise:
Rx.js предлагает оператор toPromise(), который можно использовать для преобразования Observeble<T> в Promise. После преобразования блок «then» будет выполняться всякий раз, когда есть данные.
1 2 3 4 5 6 7 |
getDataUsingPromise() { this.httpClient.get<Employee>(this.url).toPromise().then(data => { this.promiseResult = data; console.log('Promise resolved.') }); console.log('I will not wait until promise is resolved..'); } |
Получение результата HttpClient, используя async-await:
При использовании шаблона async нам не нужны ни «subscribe», ни «toPromise». Код выглядит очень просто и очевидно. Строка 3 должна выполняться после извлечения данных из «url», Observerable<T> преобразуется в Promise, и Promise разрешается, а данные сохраняются в переменной-члене «asyncResult».
1 2 3 4 |
async getAsyncData() { this.asyncResult = await this.httpClient.get<Employee>(this.url).toPromise(); console.log('No issues, I will wait until promise is resolved..'); } |
Условное программирование:
Часто приложение требует получить данные из одного URL-адреса и использовать условие для получения следующих данных. Использование кода Promise должно выглядеть примерно так:
1 2 3 4 5 6 7 8 9 10 11 12 |
getConditionalDataUsingPromise() { this.httpClient.get<Employee>(this.url).toPromise().then(data => { console.log('First Promise resolved.') if (data.id > 5) { let anotherUrl = '//dummy.restapiexample.com/api/v1/employee/23'; this.httpClient.get<Employee>(anotherUrl).toPromise().then(data => { this.conditionalPromiseResult = data; console.log('Second Promise resolved.') }); } }); } |
Использование async-await кода должно выглядеть примерно так:
1 2 3 4 5 6 7 8 |
async getConditionalDataUsingAsync() { let data = await this.httpClient.get<Employee>(this.url).toPromise(); if (data.id > 5) { let anotherUrl = '//dummy.restapiexample.com/api/v1/employee/23'; this.conditionalAsyncResult = await this.httpClient.get<Employee>(anotherUrl).toPromise(); } console.log('No issues, I will wait until promise is resolved..'); } |
Вы можете найти полный исходный код примеров в моем репозитории GitHub.
Заключение
В заключение, функция async-await предоставляет нам лучший способ написания асинхронного кода в приложении Angular.
Обновление: с более новой версией Angular нам не нужно беспокоиться о promise, возвращаемом из http(). Мы все еще можем использовать в JS async-await для другой логики, основанной на promise.
Автор: Balram Chavan
Источник: //medium.com
Редакция: Команда webformyself.