Skip to main content

Typescript 中的 promise

· 6 min read
VisualDust
Ordinary Magician | Half stack developer

Promise 是一个对象,它代表了一个异步操作的最终完成或者失败。本质上 Promise 是一个函数返回的对象,我们可以在它上面绑定回调函数,这样我们就不需要在一开始把回调函数作为参数传入这个函数了。Promise 类存在于很多现代 JavaScript 引擎中,其主要目的是为异步/回调风格的代码带来同步风格的错误处理。

假设现在有一个名为 createAudioFileAsync() 的函数,它接收一些配置和两个回调函数,然后异步地生成音频文件。一个回调函数在文件成功创建时被调用,另一个则在出现异常时被调用:

// 成功的回调函数
function successCallback(result) {
console.log("音频文件创建成功: " + result);
}

// 失败的回调函数
function failureCallback(error) {
console.log("音频文件创建失败: " + error);
}

createAudioFileAsync(audioSettings, successCallback, failureCallback)

更现代的函数会返回一个 Promise 对象,使得你可以将你的回调函数绑定在该 Promise 上。如果函数 createAudioFileAsync() 被重写为返回 Promise 的形式,那么我们可以像下面这样简单地调用它:

const promise = createAudioFileAsync(audioSettings);
promise.then(successCallback, failureCallback);

或简写为:

createAudioFileAsync(audioSettings).then(successCallback, failureCallback);

不同于“老式”的传入回调,在使用 Promise 时,会有以下约定:

  • 在本轮事件循环运行完成之前,回调函数是不会被调用的。
  • 即使异步操作已经完成(成功或失败),在这之后通过 then() 添加的回调函数也会被调用。
  • 通过多次调用then()可以添加多个回调函数,它们会按照插入顺序进行执行。
note

JS 事件循环

JavaScript有一个基于事件循环的并发模型,事件循环负责执行代码、收集和处理事件以及执行队列中的子任务。这个模型与其它语言中的模型截然不同,比如 C 和 Java。这让我这个原本写C++和Java的程序员上来很不习惯(不过运行时栈、堆的概念倒是很熟悉的味道)。详情请参阅Javascript 事件循环

"执行至完成"

对 Javascript 来说,每一个消息完整地执行后,其它消息才会被执行。这为程序的分析提供了一些优秀的特性,包括:当一个函数执行时,它不会被抢占,只有在它运行完毕之后才会去运行任何其他的代码,才能修改这个函数操作的数据。这与C语言不同,例如,如果函数在线程中运行,它可能在任何位置被终止,然后在另一个线程中运行其他代码。

Promise 很棒的一点就是链式调用(chaining)。在其他语言中,我在封装类的时候也喜欢设计一些链式调用(比如我的操作系统大作业就因为使用了过多的链式调用设计被指导老师诟病)。不过,Promisethen()方法返回的并不是原来的对象了。

warning

then() 函数会返回一个和原来不同的新的 Promise

const promise = doSomething();
const promise2 = promise.then(successCallback, failureCallback);

在上例中,promise2 不仅表示 doSomething() 函数的完成,也代表了你传入的 successCallback 或者 failureCallback 的完成,这两个函数也可以返回一个 Promise 对象,从而形成另一个异步操作,这样的话,在 promise2 上新增的回调函数会排在这个 Promise 对象的后面。基本上,每一个 Promise 都代表了链中另一个异步过程的完成。

使用 Promise 解决回调地狱

Promise 之前,要想做多重的异步操作,会导致经典的回调地狱:

doSomething(function(result) {
doSomethingElse(result, function(newResult) {
doThirdThing(newResult, function(finalResult) {
console.log('Got the final result: ' + finalResult);
}, failureCallback);
}, failureCallback);
}, failureCallback);

而使用 Promise 则可以将回调绑定在对象上,形成回调链:

doSomething().then(function(result) {
return doSomethingElse(result);
})
.then(function(newResult) {
return doThirdThing(newResult);
})
.then(function(finalResult) {
console.log('Got the final result: ' + finalResult);
})
.catch(failureCallback);
warning

一定要有返回值,否则,callback 将无法获取上一个 Promise 的结果。(如果使用箭头函数,() => x 比 () => { return x; } 更简洁一些,但后一种保留 return 的写法才支持使用多个语句。)。