在 Angular 中使用 async-await 特性

原文链接:

https://medium.com/@balramchavan/using-async-await-feature-in-angular-587dd56fdc77?

medium.com

更新:在Angular的新版本中,我们不需要担心 http()[1] 返回的 promise。尽管如此,我们仍然可以使用 async-await 来实现其他基于 promise 的逻辑。

在 JavaScript 中,用 Promises 和回调函数编写非同步代码。

在 Angular 应用中,我们可以使用 Rx.js 利用 Observables, Subject, BehaviorSubject 等强大的功能,以优雅的方式编写非同步代码。

ECMA 脚本草案的最新版本,JavaScript 开始支持 async-await

ECMAScript Latest Draft (ECMA-262)

如果你有 c# 的开发背景,你可能知道从 c# 5开始就支持 async-await 特性了。

Async-await

按照MDN

当调用非同步函数时,它返回一个 Promise 。当 async 函数返回一个值时,将使用返回的值 resolved Promise。当非同步函数抛出异常或某个值时,将使用抛出的值 rejected promise。 非同步函数可以包含一个 await 表达式,该表达式挂起非同步函数的执行并等待传递的 Promise 的解析,然后恢复非同步函数的执行并返回 resolved 后的值。

简单地说,您将有机会以同步方式编写非同步代码。

例1

让我们考虑一个简单的例子。一个函数,它在两秒后返回一个承诺解析并返回作为参数传递的值。

resolveAfter2Seconds(x) {
return new Promise(resolve => {
setTimeout(() => {
resolve(x);
}, 2000);
});
}

使用 promise ,我们可以使用 then 回调函数获得值。

getValueWithPromise() {
this.resolveAfter2Seconds(20).then(value => {
console.log(`promise result: ${value}`);
});
console.log(I will not wait until promise is resolved);
}

在上述情况下,第5行 console.log() 应在第3行 console.log() 之前执行。这是Promise的基础能力。

现在,我们看下 async-await 的用法

async getValueWithAsync() {
const value = <number>await this.resolveAfter2Seconds(20);
console.log(`async result: ${value}`);
}

这里有几点需要注意:

第1行-函数的前缀是 async 关键字。如果函数有 wait 关键字,则必须使用此前缀。

第2行-我们没有在 promise 函数之后调用 .then() 回调函数。相反,我们用 await 关键字作为函数调用的前缀。此关键字不允许执行下一个代码块。这意味著,只有当 promise 像同步函数调用一样在第2行解析时,第3行的 console.log() 才会被列印出来。

由于我们使用 Typescript,所以需要将 promise 返回值转成特定类型,因此转成第2行的类型。

例2

让我们尝试使用基于 promise 的方法添加两个数字。

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 结构(回调地狱)。我们上面的代码就出现了2级嵌套。想像一下用捕获的变数和异常(如果有的话)进行7-8级嵌套。可怕的不是吗?

现在我们使用基于 async 的方法

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 方法。

消费Http REST APIs

到目前为止,我们讨论了一些简单的例子。在 Angular 应用中,我们可以使用 Http[2] (即将过时)或 HttpClient 服务来获取 REST 数据。默认情况下,HttpClient 类的 get()put()delete()post() 方法返回 Observable<T> 。这个结果集可以通过 subscribe 方法或使用 RxJs 中的 toPromise() 操作符来使用。

使用 Observable 获取 HttpClient 结果:

我见过很多 Angular 开发人员使用 subscribe 来获取 Http REST 数据,却不知道它与 promise 的区别。 subscribe 方法来自 Observable 对象。一旦订阅,subscribe 回调将在 Observer 产生新数据时执行。而 promise 的 then() 回调处理器最多只能执行一次。因此,除非您需要使用重复数据,否则不要使用 subscribe 。使用 toPromise () 。如果你注意到 Angular 官方文档中给出的例子; toPromise 有很多用法。

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..);
}

使用 toPromise 获取 HttpClient 结果

Rx.js 提供了一个名为 toPromise() 的操作符,可以将 Observeble<T> 转换成 promise。一旦转换后,它的 then 块每当方法产生数据时会被调用执行。

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..);
}

使用 async-await 获得 HttpClient 结果:

使用 async-await 模式,我们既不需要 subscribe 也不需要 toPromise 。代码看起来非常简单和直接。从 url 获取数据后,执行第3行,将 Observerable 转换为 promise,并解析promise,数据存储在 asyncResult 成员变数中。

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 代码应该是这样的:

getConditionalDataUsingPromise() {
this.httpClient.get<Employee>(this.url).toPromise().then(data => {
console.log(First Promise resolved.)
if (data.id > 5) {
let anotherUrl = http://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 代码是这样的:

async getConditionalDataUsingAsync() {
let data = await this.httpClient.get<Employee>(this.url).toPromise();
if (data.id > 5) {
let anotherUrl = http://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 仓库获取完整代码

ultrasonicsoft/ng-async-await-demo?

github.com

总结

总之,async-await 特性为我们在 Angular 应用程序中编写非同步代码提供了更好的方法。

参考

  1. ^译者注:8.0已经完全移除@angular/http
  2. ^已过时

推荐阅读:

相关文章