参考链接
史上最易读懂的 Promise/A+ 完全实现
30分钟,让你彻底明白Promise原理
深入理解 Promise (中)
这篇文章主要讲代码实现以及个人理解,原理部分可以参照上面链接。
基础结构及注意事项
function Promise(executor) {
var self = this
self.status = 'pending' // Promise当前的状态
self.data = undefined // Promise的值
self.onResolvedCallback = [] // Promise resolve时的回调函数集,因为在Promise结束之前有可能有多个回调添加到它上面
self.onRejectedCallback = [] // Promise reject时的回调函数集,因为在Promise结束之前有可能有多个回调添加到它上面
executor(resolve, reject) // 执行executor并传入相应的参数
}
到这里,我们实现了Promise的基本雏形。从上面的构造方法中可以看出,我们传入的excutor在new Promise()时就会被执行。
那我们来看下面这个场景:
function getUserId() {
return new Promise(function(resolve) {
//异步请求
http.get(url, function(results) {
resolve(results.id)
})
})
}
getUserId().then(function(id) {
console.log(id)
//一些处理
})
在这个例子中,http.get(url, function(results) {resolve(results.id)})在new Promise时就被执行了,里面的回调函数function(results) {resolve(results.id)}被放入事件循环队列中,等待主线程空闲时执行。注意哟,这个任务是不会被添加到onResolvedCallback/onRejectedCallback队列哟。
实现resolve和reject方法
resolve和reject方法做的事情很简单,修改promise状态,然后分别将onResolvedCallback/onRejectedCallback的函数全部遍历并执行一遍。
function Promise(executor) {
var self = this
self.status = 'pending' // Promise当前的状态
self.data = undefined // Promise的值
self.onResolvedCallback = [] // Promise resolve时的回调函数集,因为在Promise结束之前有可能有多个回调添加到它上面
self.onRejectedCallback = [] // Promise reject时的回调函数集,因为在Promise结束之前有可能有多个回调添加到它上面
executor(resolve, reject) // 执行executor并传入相应的参数
function resolve(value) {
if (self.status === 'pending') {
self.status = 'resolved'
self.data = value
for(var i = 0; i < self.onResolvedCallback.length; i++) {
self.onResolvedCallback[i](value)
}
}
}
function reject(reason) {
if (self.status === 'pending') {
self.status = 'rejected'
self.data = reason
for(var i = 0; i < self.onRejectedCallback.length; i++) {
self.onRejectedCallback[i](reason)
}
}
}
}
实现then方法
then方法的工作其实也很简单,就是注册回调函数,并返回一个新的Promise对象. 我们思考这样两个问题:
为什么要返回Promise?
为什么时返回新的Promise,而不是原来的Promsie???
对于第一个问题,我们很容易想到返回Promsie是为了支持链式调用。在Promises/A+规范中的2.1Promise States中明确规定了,pending可以转化为fulfilled或rejected并且只能转化一次,也就是说如果pending转化到fulfilled状态,那么就不能再转化到rejected。并且fulfilled和rejected状态只能由pending转化而来,两者之间不能互相转换。一图胜千言:

Promsie的状态是不可逆的。
Promise.prototype.then = function (onResolved, onRejected) {
var self = this;
// 根据标准,如果then的参数不是function,则我们需要忽略它,此处以如下方式处理
onResolved = typeof onResolved === 'function' ? onResolved : function (v) { }
onRejected = typeof onRejected === 'function' ? onRejected : function (r) { }
//
switch (self.status) {
case 'resolved':
return new Promise(function (resolve, reject) {
try {
var x = onResolved(self.data)
if (x instanceof Promise) { // 如果onResolved的返回值是一个Promise对象,直接取它的结果做为结果
x.then(resolve, reject)
}
resolve(x) // 否则,以它的返回值作为参数传递
} catch (e) {
reject(e) // 如果出错,以捕获到的错误做为结果
}
})
case 'rejected':
return new Promise(function (resolve, reject) {
try {
var x = onRejected(self.data)
if (x instanceof Promise) {
x.then(resolve, reject)
}
} catch (e) {
reject(e)
}
})
default: // 'pending状态'
return new Promise(function (resolve, reject) {
self.onResolvedCallback.push(function (value) {
try {
var x = onResolved(self.data)
if (x instanceof Promise) {
x.then(resolve, reject)
}
} catch (e) {
reject(e)
}
});
self.onRejectedCallback.push(function (reason) {
try {
var x = onRejected(self.data)
if (x instanceof Promise) {
x.then(resolve, reject)
}
} catch (e) {
reject(e)
}
})
})
}
我们回过头来看下面这个场景:
function getUserId() {
return new Promise(function(resolve) {
//异步请求
http.get(url, function(results) {
resolve(results.id)
})
})
}
getUserId().then(function(id) {
console.log(id)
//一些处理
})
我们知道getUserId()是一个异步方法,其返回值是一个Promsie对象(暂且称之为A),当代码执行到getUserId().then()时,A的状态是未知的,可能是pending,可能是resolved,也可能是rejected。因此在上面,我们对3中情况都进行了处理:
pending状态。向onResolvedCallback/onRejectedCallback队列中注册回调函数。
resolved状态,执行onResolved函数。如果onResolve函数的返回值仍然是一个Promise,则递归调用then方法。 如果不是,用它的返回值作为结果。也许你想知道为什么这样做,其实也很简单,Promsie其实就是类似于流,对上面传来的数据进行加工处理。 现在,我们来看这样一种情形:获取用户id,然后根据用户id去获取购物车数据。
function getUserId() {
return new Promise(function(resolve) {
//异步请求
http.get(url, function(results) {
resolve(results.id)
})
})
}
getUserId().then(function(id) {
return Promise(function(resolve){
http.get(url/:id,function(results){
resolve(results)
}
})
}).then((val)=>{
console.log(val)
})
rejected状态。与resolved原理差不多,在此不赘诉。
bug修复(加入延时机制)
细心的同学应该发现,上述代码可能还存在一个问题:如果在then方法注册回调之前,resolve函数就执行了,怎么办?比如promise内部的函数是同步函数:
// 例3
function getUserId() {
return new Promise(function (resolve) {
resolve(9876);
});
}
getUserId().then(function (id) {
// 一些处理
});
这显然是不允许的,Promises/A+规范明确要求回调需要通过异步方式执行,用以保证一致可靠的执行顺序。因此我们要加入一些处理,保证在resolve执行之前,then方法已经注册完所有的回调。我们可以这样改造下resolve函数:
function resolve(value) {
setTimeout(function(){
if (self.status === 'pending') {
self.status = 'resolved'
self.data = value
for(var i = 0; i < self.onResolvedCallback.length; i++) {
self.onResolvedCallback[i](value)
}
}
},0)
}
同样的,对于rejected函数,我们也需要进行同样的处理
function reject(reason) {
setTimeout(function(){
if (self.status === 'pending') {
self.status = 'rejected'
self.data = reason
for(var i = 0; i < self.onRejectedCallback.length; i++) {
self.onRejectedCallback[i](reason)
}
}
},0)
}
至此,我们已经完成了Promise的基本结构和功能....
完整代码如下
function Promise(executor) {
var self = this
self.status = 'pending' // Promise当前的状态
self.data = undefined // Promise的值
self.onResolvedCallback = [] // Promise resolve时的回调函数集,因为在Promise结束之前有可能有多个回调添加到它上面
self.onRejectedCallback = [] // Promise reject时的回调函数集,因为在Promise结束之前有可能有多个回调添加到它上面
executor(resolve, reject) // 执行executor并传入相应的参数
function resolve(value) {
setTimeout(function(){
if (self.status === 'pending') {
self.status = 'resolved'
self.data = value
for(var i = 0; i < self.onResolvedCallback.length; i++) {
self.onResolvedCallback[i](value)
}
}
},0)
}
function reject(reason) {
setTimeout(function(){
if (self.status === 'pending') {
self.status = 'rejected'
self.data = reason
for(var i = 0; i < self.onRejectedCallback.length; i++) {
self.onRejectedCallback[i](reason)
}
}
},0)
}
}
Promise.prototype.then = function (onResolved, onRejected) {
var self = this;
// 根据标准,如果then的参数不是function,则我们需要忽略它,此处以如下方式处理
onResolved = typeof onResolved === 'function' ? onResolved : function (v) { }
onRejected = typeof onRejected === 'function' ? onRejected : function (r) { }
//
switch (self.status) {
case 'resolved':
return new Promise(function (resolve, reject) {
try {
var x = onResolved(self.data)
if (x instanceof Promise) { // 如果onResolved的返回值是一个Promise对象,直接取它的结果做为结果
x.then(resolve, reject)
}
resolve(x) // 否则,以它的返回值作为参数传递
} catch (e) {
reject(e) // 如果出错,以捕获到的错误做为结果
}
})
case 'rejected':
return new Promise(function (resolve, reject) {
try {
var x = onRejected(self.data)
if (x instanceof Promise) {
x.then(resolve, reject)
}
} catch (e) {
reject(e)
}
})
default: // 'pending状态'
return new Promise(function (resolve, reject) {
self.onResolvedCallback.push(function (value) {
try {
var x = onResolved(self.data)
if (x instanceof Promise) {
x.then(resolve, reject)
}
} catch (e) {
reject(e)
}
});
self.onRejectedCallback.push(function (reason) {
try {
var x = onRejected(self.data)
if (x instanceof Promise) {
x.then(resolve, reject)
}
} catch (e) {
reject(e)
}
})
})
}