本文针对 Promise 对象的原理进行分析,来实现一个 Promise 对象。
首先我们要理解什么 是 Promise ?
Promises 对象是 CommonJS 工作组提出的一种规范, 目的是为异步编程提供统一接口; 是 javascript 异步编程的重要概念,也是目前 javascript 主流的异步编程方法中的一种。
常见的 javascript 异步编程的有以下四种方法:
- callback
- 事件监听
- 发布/订阅 (观察者模式)
- Promise
我们常用的 callback 是异步操作最基本的方法
举个例子,项目中要求前端 ajax 请求接口拿到产品订单号, 在根据产品订单号请求接口拿到 产品类型名称, 在根据产品的类型请求接口拿到产品的 code ,代码大概如下
$.ajax({ url: "", data: 1, success: function (orderId) { $.ajax({ url: "", data: orderId, success: function (code) { $.ajax({ url: "", data: code, success: function (c) { console.log(c); } }) } }) } });
- 这种写法每个请求之间相互依赖,层层嵌套形成了‘ 回调地狱 ’, 若在加上业务代码, 可读性与可维护性可想而知。而 Promise 的出现简化这种
回调地狱
式的编程模式, 它可以处理多个相互关联的异步操作,将异步代码抽离出来,实现了回调函数链式写法。
// 代码改编之后, 让流程看起来更加清晰 let promise = new Promise(function(resolve, reject){ $.ajax({ url: "", data: 1, success: function (data) { resolve(data); } }); }); promise.then( data1 => { $.ajax({ url: "", data: data1, success: function (data) { return data } }); } ).then( data2 => { console.log( 'data2', data2 ); } ).catch(err => { console.log( 'err', err ); });
- 这种写法每个请求之间相互依赖,层层嵌套形成了‘ 回调地狱 ’, 若在加上业务代码, 可读性与可维护性可想而知。而 Promise 的出现简化这种
Promise 的用法
Promise 是一个对象/构造函数,new Promise 传入 executor 执行函数作为参数,executor 函数接受两个函数 resolve, reject 作为参数, 返回一个 promise 实例
let promise = new Promise(function(resolve, reject){ // 处理一些异步的操作, 成功 执行 resolve, 失败执行 reject resolve('成功'); });
promise 有三种状态
- 默认态为 pending(等待态)
- 成功态为 fulfilled
- 失败态为 rejected
- promise 对象的默认状态为 pending, 当调用 resolve 时, 状态由 pending => fulfilled; 当调用 reject 时, 状态由 pending => rejected; 状态由 pending 装为 fulfilled 或 rejected 时,状态转变之后不能更改Promise 实例上 存在一个 then 方法,可以调用 then 方法来处理回调操作
// then 方法接受两个参数, onFulfilled, onRejected ,都为可选参数 // then 方法执行后返回一个 promise 实例, 可以实现链式调用 promise.then( data => { // promise 执行结束后(resolve)当前函数会被调用,data为 resolve 执行时传入的参数 console.log(data); }, err => { // promise 执行被拒绝后(reject)当前函数会被调用,err为 resolve 被拘的理由 console.log(err); } ).then();
promise 实例上存在一个 catch 的方法, 用来捕获抛出的错误
promise.catch(onRejected) 相当于 promise.then(null, onRejected);
promise.then(onFulfilled, onRejected) onRejected 不能捕获 onFulfilled 中抛出的错误, 但 catch 可以捕获到
Promise 上还存在一些 静态方法
/**
* Promise.all 执行的结果全部为 fulfilled 时, 才会走到 then 中成功回调
* 参数接受一个数组, 数组中的每一项需为一个 promise 实例
* 通常用来处理多个并行的异步操作
*/
function read (url) {
return new Promise(function(resolve, reject){
require('fs').readFile(url, 'utf-8', function(err, data){
if (err) reject(err);
resolve(data);
})
});
}
Promise.all([read('./1.txt'), read('./2.txt')]).then(data => {
// ...
}, err => {
// ...
});
/**
* Promise.race 执行的结果有一个为 rejected 时, 就会调用 then 中的失败回调
* 参数接受一个数组, 数组中的每一项需为一个 promise 实例
*/
Promise.race([read('./1.txt'), read('./2.txt')]).then(data => {
// ...
}, err => {
// ...
});
/**
* Promise.resolve
* 类似语法糖 返回一个成功态的 promise
*/
Promise.resolve(value).then(onFulfilled);
/**
* Promise.reject
* 类似语法糖 返回一个失败态的 promise
*/
Promise.reject(reason).then(null, onRejected);
Promise 代码分析
function Promise (executor) {
// executor 函数执行器, 是同步执行的
let self = this; // 防止 this 指针出现问题
self.status = 'pending'; // promise 默认的状态
self.value = undefined; // 存储 fulfilled 时对应的值
self.reason = undefined; // 存储 fulfilled 时对应的原因
self.onFulfilledCallbacks = []; // 存放 fulfilled 状态时 then 中 onFulfilled 的回调函数
self.onRejectedCallbacks = []; // 存放 rejected 状态时 then 中 onRjected 的回调函数
// 内部定义 成功时 执行的 函数
function resolve (value) {
// 根据 PromiseA+ 规划中 1.3 描述 value 可以是 thenable,这时需递归处理, 直到 value 为普通的值
if ( value instanceof Promise ) {
return value.then(resolve, reject);
}
// 根据 PromiseA+ 规范 2.1 描述 状态在 pending 时可以转为 成功或失败
if (self.status === 'pending') {
self.status = 'fulfilled';
self.value = value;
// 让存储的 onFulfilled 的回调依次执行
self.onFulfilledCallbacks.forEach(function (fn) {
fn();
});
}
}
// 内部定义 成功时 执行的 函数
function reject (reason) {
if (self.status === 'pending') {
self.status = 'rejected';
self.reason = reason;
// 让存储的 onRjected 的回调依次执行
self.onRejectedCallbacks.forEach( function (fn) {
fn()
} );
}
}
// 捕获 executor 函数执行器执行时抛出的错误
try {
executor(resolve, reject);
} catch (e) {
reject(e);
}
}
/**
* 注册 fulfilled 状态 / rejected 状态时对应的回调函数
* @param {function} onFulfilled 状态为 fulfilled 时执行的回调
* @param {function} onRjected 状态为 rejected 时执行的回调
*/
Promise.prototype.then = function (onFulfilled, onRejected) {
let self = this;
let promise2;
// 根据PromiseA+ 规范2.2.1描述, onFulfilled / onRjected是可选参数,因此可以存在值穿透的情况
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (value) { return value };
onRejected = typeof onRejected === 'function' ? onRejected : function (err) { throw err };
if (self.status === 'fulfilled') {
// 根据 PromiseA+ 规范2.2.7描述, then 方法的返回值必须是一个 promise 对象, 可是实现链式调用, 因此 then 方法执行后需返回一个新的 promise
promise2 = new Promise(function (resolve, reject) {
// 根据 PromiseA+ 规范 2.2.4 描述 ,onFulfilled 和 onRejected 只有在执行环境堆栈仅包含平台代码时才可被调(被调用的那一轮事件循环之后的新执行栈中执行)
setTimeout(function(){
try {
// onFulfilled 执行的结果可能是一个普通值,或是一个新 promise 对象或函数,或是调用别人的 promise,需要进行判断,针对不同的值进行处理
let x = onFulfilled(self.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
})
});
}
if (self.status === 'rejected') {
promise2 = new Promise(function (resolve, reject) {
setTimeout(function () {
try {
let x = onRejected(self.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
})
})
}
if (self.status === 'pending') {
// 如果 执行器 executor 中有异步操作而 promise 的状态属于 pending 时,这时不知道会转化为 fulfilled 还是 rejected,可以先将 onFulfilled 跟 onRjected 存在定义好的数组中, 当 pending 状态转变时, 执行对应的 callback
promise2 = new Promise(function (resolve, reject) {
self.onFulfilledCallbacks.push(function () {
setTimeout(function(){
try {
let x = onFulfilled(self.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
})
});
self.onRejectedCallbacks.push(function () {
setTimeout(function () {
try {
let x = onRejected(self.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
})
});
});
}
return promise2;
}
/**
* 针对 fulfilled 状态 / rejected 状态时执行的结果 x 进行统一处理
* @param {promise} promise2
* @param {*} x
* @param {function} resolve promise2 fulfilled 时执行的 resolved
* @param {funtion} reject promise2 rejected 时执行的 reject
*/
function resolvePromise (promise2, x, resolve, reject) {
let called; // 有的 promise 中会调用两个状态对应的回调,这时需定义一个标识,resolve 与 reject 只执行一个
// 根据 promiseA+ 规范 2.3.1 描述 如果 x = promise2 , 抛出类型错误
if (promise2 === x) {
return reject(new TypeError('循环引用'));
}
if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
// 检测 x 上是否存在 then 函数
try {
let then = x.then;
if (typeof then === 'function') {
then.call(x, function (y) { // PromiseA+ 2.3.3
if (called) return;
called = true;
// 此时的 y 可能还是一个 promise, 再去解析直到返回的是一个普通值
resolvePromise(promise2, y, resolve, reject);
}, function (err) {
// rejected 态
if (called) return
called = true
reject(err);
});
}
else {
resolve(x);
}
} catch (e) {
if (called) return
called = true;
reject(e);
}
}
else {
// 如果 x 是一个普通值的话,promise2 走向 fulfilled 态
resolve(x);
}
}
/**
* 捕获错误的方法
* @param {function} callback 状态为 rejected 时执行的回调
*/
Promise.prototype.catch = function (callback) {
return this.then(null, callback);
}
// ---------------------- Promise 上的静态方法 ------------------------
/**
* 执行的结果全部为 fulfilled 状态时, 才会走到 then 中 onFulfilled 对应的回调
* @param {*} promiseAry 接受一个数组,数组中的每一项需为一个 promise 实例, 返回一个新的 promise 实例
* 通常用来处理多个并行的异步操作
*/
Promise.all = function (promiseAry) {
let arr = [];
let i = 0;
return new Promise(function (resolve, reject){
function processData (index, y) {
arr[index] = y;
if ( ++i === promiseAry.length ) {
resolve(arr);
}
}
for ( let i = 0; i < promiseAry.length; i++ ) {
promiseAry[i].then(function (y) {
processData(i, y);
}, reject)
}
});
}
/**
* Promise.race 执行的结果有一个为 rejected 时, 就会调用 then 中的 onReject 对应的回调
* @param {*} promiseAry 参数接受一个数组,数组中的每一项需为一个 promise 实例, 返回一个新的 promise 实例
*/
Promise.race = function (promiseAry) {
return new Promise(function (resolve, reject) {
for (let i = 0; i < promiseAry.length; i++) {
promiseAry[i].then(resolve, reject);
}
});
}
/**
* 语法糖, 返回一个 状态为 fulfilled 时的 promise 实例
* @param {*} value
*/
Promise.resolve = function (value) {
return new Promise(function(resolve, reject){
resolve(value);
});
}
/**
* 语法糖, 返回一个 状态为 rejected 时的 promise 实例
* @param {*} reason
*/
Promise.reject = function (reason) {
return new Promise(function (resolve, reject) {
reject(reason);
});
}
/**
* 在 Promise 上增加一个 deferred 方法,
* 返回的 deferred 对象上拥有一个 Promise 对象, 具有对 Promise 状态操作的方法
* 参考 jQuery 上 deferred 的实现
* 参考文档 http://www.ruanyifeng.com/blog/2011/08/a_detailed_explanation_of_jquery_deferred_object
*/
Promise.defer = Promise.deferred = function () {
let dfd = {};
dfd.promise = new Promise(function (resolve, reject) {
dfd.resolve = resolve;
dfd.reject = reject;
});
return dfd;
}
可以下载一个基于 PromiseA+规范的 Promise 测试库,
npm install promises-aplus-tests -g
promises-aplus-tests 文件名
参考文档:
PromiseA+规范
PromiseA+规范 译文
deferred
Promise对象