1.3.11 使用Promise和async/await创建异步代码

我们常常需要编写以异步方式工作的代码。异步工作是指我们需要先启动一个任务,让它在后台运行,同时去做另外一些工作。例如,我们调用了某个Web服务,而这种调用可能需要一段时间才能返回响应。对于这种场景,在很长一段时间内,JavaScript中的标准方法是使用回调。然而,使用回调有一个大问题:我们需要的回调越多,代码就变得越复杂、越容易出错。这时候就可以使用Promise。

Promise告诉我们将会执行异步操作。当异步操作完成后,我们可以继续处理并使用Promise的结果,或者捕捉Promise抛出的任何异常。

下面给出了一个具体示例:

当我们编写一个Promise的时候,可以选择提供两个函数——resolve,以及reject,可以调用reject函数来触发错误处理。Promise提供了两个函数then()和catch()供我们处理这些参数值。成功完成操作后会触发then()函数,catch()函数则用来处理reject()函数。

接下来,我们运行这段代码来查看其效果:

运行这段代码后,将得到下面的输出:

在输出Processing continues until the web service returns和Finished web service之间,有4秒的延迟,这符合我们的预期,因为在输出then()函数中的文本前,应用程序在等待Promise返回。这段输出表明代码是异步工作的,因为它不等待Web服务调用返回,就执行了控制台输出。

我们可能认为这段代码有点冗长,并且在不同的位置散布Promise<void>不利于其他人理解代码是异步工作的。TypeScript提供了一种在语法上等效的结构,能够更清晰地表明代码是异步的。通过使用async和await关键字,可以轻松地让前面的示例变得更加整洁:

async关键字指出函数将返回Promise,并且会告诉编译器以不同的方式处理该函数。当async函数内出现await时,应用程序将会在该位置暂停函数执行,直到等待的操作返回。返回后,处理将继续进行,类似我们在Promise的then()函数中看到的行为。

要捕捉async/await中出现的错误,需要把函数内的代码放到一个try…catch块中。当catch()函数显式捕捉到错误时,async/await没有处理问题的方法,所以要由我们来处理问题:

选择使用哪种方法由个人决定。使用async/await只是意味着将Promise方法包装起来,这两种不同方法的运行时行为完全相同。不过,建议一旦决定在一个应用程序中使用一种方法,就坚持使用这种方法。不要混用不同的风格,否则其他人会更难理解应用程序。