Javascript 采用回调函数(callback)来处理异步编程。从同步编程到异步回调编程有一个适应的过程,但是如果出现多层回调嵌套,也就是我们常说的厄运的回调金字塔(Pyramid of Doom),绝对是一种糟糕的编程体验。于是便有了 CommonJS 的 Promises/A 规范,用于解决回调金字塔问题。本文先介绍 Promises 相关规范,然后再通过解读一个迷你的 Promises 以加深理解。
什么是 Promise
一个 Promise 对象代表一个目前还不可用,但是在未来的某个时间点可以被解析的值。它允许你以一种同步的方式编写异步代码。例如,如果你想要使用 Promise API 异步调用一个远程的服务器,你需要创建一个代表数据将会在未来由 Web 服务返回的 Promise 对象。唯一的问题是目前数据还不可用。当请求完成并从服务器返回时数据将变为可用数据。在此期间,Promise 对象将扮演一个真实数据的代理角色。接下来,你可以在 Promise 对象上绑定一个回调函数,一旦真实数据变得可用这个回调函数将会被调用。
Promise 对象曾经以多种形式存在于许多语言中。
去除厄运的回调金字塔(Pyramid of Doom)
Javascript 中最常见的反模式做法是回调内部再嵌套回调。
// 回调金字塔 asyncOperation(function(data){ // 处理 `data` anotherAsync(function(data2){ // 处理 `data2` yetAnotherAsync(function(){ // 完成 }); }); });
引入 Promises 之后的代码
promiseSomething() .then(function(data){ // 处理 `data` return anotherAsync(); }) .then(function(data2){ // 处理 `data2` return yetAnotherAsync(); }) .then(function(){ // 完成 });
Promises 将嵌套的 callback,改造成一系列的.then的连缀调用,去除了层层缩进的糟糕代码风格。Promises 不是一种解决具体问题的算法,而已一种更好的代码组织模式。接受新的组织模式同时,也逐渐以全新的视角来理解异步调用。
各个语言平台都有相应的 Promise 实现
Java's java.util.concurrent.Future
Python's Twisted deferreds and PEP-3148 futures
F#'s Async
.Net's Task
C++ 11's std::future
Dart's Future
Javascript's Promises/A/B/D/A+
下面我来相信了解一下 javascript 语言环境下各个规范的一些细节。
Promises/A 规范
promise 表示一个最终值,该值由一个操作完成时返回。
promise 有三种状态:**未完成** (unfulfilled),**完成** (fulfilled) 和**失败** (failed)。
promise 的状态只能由**未完成**转换成完成,或者**未完成**转换成**失败** 。
promise 的状态转换只发生一次。
promise 有一个 then 方法,then 方法可以接受 3 个函数作为参数。前两个函数对应 promise 的两种状态 fulfilled 和 rejected 的回调函数。第三个函数用于处理进度信息(对进度回调的支持是可选的)。
promiseSomething().then(function(fulfilled){ //当promise状态变成fulfilled时,调用此函数 },function(rejected){ //当promise状态变成rejected时,调用此函数 },function(progress){ //当返回进度信息时,调用此函数 });
如果 promise 支持如下连个附加方法,称之为可交互的 promise
get(propertyName)
获得当前 promise 最终值上的一个属性,返回值是一个新的 promise。
call(functionName, arg1, arg2, ...)
调用当然 promise 最终值上的一个方法,返回值也是一个新的promise。
Promises/B 规范
在 Promises/A 的基础上,Promises/B 定义了一组 promise 模块需要实现的 API
when(value, callback, errback_opt)
如果 value 不是一个 promise ,那么下一事件循环callback会被调用,value 作为 callback 的传入值。如果 value 是一个 promise,promise 的状态已经完成或者变成完成时,那么下一事件循环 callback 会被调用,resolve 的值会被传入 callback;promise 的状态已经失败或者变成失败时,那么下一事件循环 errback 会被调用,reason 会作为失败的理由传入 errback。
asap(value, callback, errback_opt)
与 when 最大的区别,如果 value 不是一个 promise,会被立即执行,不会等到下一事件循环。
enqueue(task Function)
尽可能快地在接下来的事件循环调用 task 方法。
get(object, name)
返回一个获得对象属性的 promise。
post(object, name, args)
返回一个调用对象方法的 promise。
put(object, name, value)
返回一个修改对象属性的 promise。
del(object, name)
返回一个删除对象属性的 promise。