上一篇文章中,我们介绍了Promise的基本使用,在这篇文章中,我们试着自己来写一个Promise,主要是学习Promise的内部机制,学习它的编程思想。
!!!备注:本文写的不好,仅供自己学习之用,具体的实现过程建议看下面的参考文章。所以本文没有发布到博客园首页和其他地方
Promise API分析
正常使用方法
我们来看一个正常的使用:
var p=new Promise(function(resolve,rejcet){
setTimeout(function(){
if(true){
resolve('success');
}else{
rejcet('failure');
}
},1000);
});
p.then(function(value){
console.log(value);
},function(error){
console.log(error);
});
//success
接下来我们就来实现这么一个Promise.
先来了解相关的一些术语:
解决(fulfill):指一个 promise 成功时进行的一系列操作,如状态的改变、回调的执行。虽然规范中用 fulfill 来表示解决,但在后世的 promise 实现多以 resolve 来指代之。
拒绝(reject):指一个 promise 失败时进行的一系列操作。
终值(eventual value):所谓终值,指的是 promise 被解决时传递给解决回调的值,由于 promise 有一次性的特征,因此当这个值被传递时,标志着 promise 等待态的结束,故称之终值,有时也直接简称为值(value)。
据因(reason):也就是拒绝原因,指在 promise 被拒绝时传递给拒绝回调的值。
Promise的流程图分析
promise的执行流程如如下:
Promise链式操作中,执行顺序是如何保证的
每个promise后面链一个对象该对象包含onfulfiled,onrejected,子promise三个属性,当父promise 状态改变完毕,执行完相应的onfulfiled/onfulfiled的时候呢,拿到子promise,在等待这个子promise状态改变,再执行相应的onfulfiled/onfulfiled。依次循环直到当前promise没有子promise
如何让异步的value在thenable函数中拿到
将resolve/reject函数和onfulfiled/onrejected放入同一个对象(promise对象)里面,resolve/reject的时候将value设置this.value=xxx。onfulfiled/onrejected执行的时候呢,onfulfiled(this.value)即可。
在这里避免头晕,解释一下,onfulfilled和onrejected指的是then里面的两个函数。
状态机制切换
如图所示,状态只能由pengding-->fulfilled,或者由pending-->rejected这样转变。
只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。就算改变已经发生了,你再对Promise
对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
开始手写Promise
1、 首先我们来写好我们的框架
ES6原生的构建方式为:
// Promise构造函数接收一个executor函数,executor函数执行完同步或异步操作后,调用它的两个参数resolve和reject
var promise = new Promise(function(resolve, reject) {
/*
如果操作成功,调用resolve并传入value
如果操作失败,调用reject并传入reason
*/
})
我们就按照这种方式来搭好框架
function Promise(callback) {
var self = this
self.status = 'PENDING' // Promise当前的状态
self.data = undefined // Promise的值
self.onResolvedCallback = [] // Promise resolve时的回调函数集
self.onRejectedCallback = [] // Promise reject时的回调函数集
callback(resolve, reject) // 执行executor并传入相应的参数
function resolve(value){
}
function rejecte(error){
}
}
// 添加我们的then方法
Promise.prototype.then=function(){
}
我们构造一个Promise函数,并传入一个回调callback,callback里面传入两个函数作为参数,一个是resove,一个是reject。并在Promise的原型上加入我们的then方法。
2、完善框架里面的内容
框架搭好了,接下来我们来一点点的完善框架里面的内容,可以这么说,把resolve,reject和then补充完,基本可以说就是把Promise完成了。
我们先来完善我们的resolve和rejected:
function Promise(callback) {
var self = this
self.status = 'PENDING' // Promise当前的状态
self.data = undefined // Promise的值
self.onResolvedCallback = [] // Promise resolve时的回调函数集
self.onRejectedCallback = [] // Promise reject时的回调函数集
callback(resolve, reject) // 执行executor并传入相应的参数
function resolve(value){
if(self.status=='PENDING'){
self.status=='FULFILLED';
self.data=value;
// 依次执行成功之后的函数栈
for(var i = 0; i < self.onResolvedCallback.length; i++) {
self.onResolvedCallback[i](value)
}
}
}
function rejecte(error){
if (self.status === 'PENDING') {
self.status = 'REJECTED'
self.data = error;
// 依次执行失败之后的函数栈
for(var i = 0; i < self.onRejectedCallback.length; i++) {
self.onRejectedCallback[i](error)
}
}
}
}
如果是penging,则改变相应的状态,并把resolve和reject的值保存子data里面。
接下来我们实现我们的then方法:
then方法是Promise的核心,因此这里会花比较大的篇幅去介绍then:
一个promise的then接受两个参数:
promise.then(onFulfilled, onRejected)
onFulfilled
和 onRejected
都是可选参数。
- 如果
onFulfilled
不是函数,其必须被忽略 - 如果
onRejected
不是函数,其必须被忽略
onFulfilled
特性
如果 onFulfilled
是函数:
- 当
promise
执行结束后其必须被调用,其第一个参数为promise
的终值,也就是resolve传过来的值 - 在
promise
执行结束前其不可被调用 - 其调用次数不可超过一次
onRejected
特性
如果 onRejected
是函数:
- 当
promise
被拒绝执行后其必须被调用,其第一个参数为promise
的据因,也就是reject传过来的值 - 在
promise
被拒绝执行前其不可被调用 - 其调用次数不可超过一次
调用时机
onFulfilled
和 onRejected
只有在执行环境堆栈仅包含平台代码时才可被调用(平台代码指引擎、环境以及 promise 的实施代码)
调用要求
onFulfilled
和 onRejected
必须被作为函数调用(即没有 this
值,在 严格模式(strict) 中,函数 this
的值为 undefined
;在非严格模式中其为全局对象。)
多次调用
then
方法可以被同一个 promise
调用多次
- 当
promise
成功执行时,所有onFulfilled
需按照其注册顺序依次回调 - 当
promise
被拒绝执行时,所有的onRejected
需按照其注册顺序依次回调
返回
then
方法必须返回一个 promise
对象
promise2 = promise1.then(onFulfilled, onRejected);
- 如果
onFulfilled
或者onRejected
返回一个值x
,则运行下面的 Promise 解决过程:[[Resolve]](promise2, x)
- 如果
onFulfilled
或者onRejected
抛出一个异常e
,则promise2
必须拒绝执行,并返回拒因e
- 如果
onFulfilled
不是函数且promise1
成功执行,promise2
必须成功执行并返回相同的值 - 如果
onRejected
不是函数且promise1
拒绝执行,promise2
必须拒绝执行并返回相同的据因
不论 promise1
被 reject 还是被 resolve 时 promise2
都会被 resolve,只有出现异常时才会被 rejected。
每个Promise对象都可以在其上多次调用then方法,而每次调用then返回的Promise的状态取决于那一次调用then时传入参数的返回值,所以then不能返回this,因为then每次返回的Promise的结果都有可能不同。
接下来我们来写我们的then方法:
Promise.prototype.then = function(onResolved, onRejected) {
var self = this
var promise2
// 根据标准,如果then的参数不是function,则我们需要忽略它,此处以如下方式处理
onResolved = typeof onResolved === 'function' ? onResolved : function(value) {}
onRejected = typeof onRejected === 'function' ? onRejected : function(reason) {}
if (self.status === 'resolved') {
// 如果promise1(此处即为this/self)的状态已经确定并且是resolved,我们调用onResolved
// 因为考虑到有可能throw,所以我们将其包在try/catch块里
return promise2 = new Promise(function(resolve, reject) {
try {
var x = onResolved(self.data)
if (x instanceof Promise) { // 如果onResolved的返回值是一个Promise对象,直接取它的结果做为promise2的结果
x.then(resolve, reject)
}
resolve(x) // 否则,以它的返回值做为promise2的结果
} catch (e) {
reject(e) // 如果出错,以捕获到的错误做为promise2的结果
}
})
}
// 此处与前一个if块的逻辑几乎相同,区别在于所调用的是onRejected函数,就不再做过多解释
if (self.status === 'rejected') {
return promise2 = new Promise(function(resolve, reject) {
try {
var x = onRejected(self.data)
if (x instanceof Promise) {
x.then(resolve, reject)
}
} catch (e) {
reject(e)
}
})
}
if (self.status === 'pending') {
// 如果当前的Promise还处于pending状态,我们并不能确定调用onResolved还是onRejected,
// 只能等到Promise的状态确定后,才能确实如何处理。
// 所以我们需要把我们的**两种情况**的处理逻辑做为callback放入promise1(此处即this/self)的回调数组里
// 逻辑本身跟第一个if块内的几乎一致,此处不做过多解释
return promise2 = 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)
}
})
})
}
}
// 为了下文方便,我们顺便实现一个catch方法
Promise.prototype.catch = function(onRejected) {
return this.then(null, onRejected)
}
完整代码如下:
作者:谢然View Code
链接:https://zhuanlan.zhihu.com/p/21834559
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
try {
module.exports = Promise
} catch (e) {}
function Promise(executor) {
var self = this
self.status = 'pending'
self.onResolvedCallback = []
self.onRejectedCallback = []
function resolve(value) {
if (value instanceof Promise) {
return value.then(resolve, reject)
}
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)
}
}
})
}
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)
}
}
})
}
try {
executor(resolve, reject)
} catch (reason) {
reject(reason)
}
}
function resolvePromise(promise2, x, resolve, reject) {
var then
var thenCalledOrThrow = false
if (promise2 === x) {
return reject(new TypeError('Chaining cycle detected for promise!'))
}
if (x instanceof Promise) {
if (x.status === 'pending') { //because x could resolved by a Promise Object
x.then(function(v) {
resolvePromise(promise2, v, resolve, reject)
}, reject)
} else { //but if it is resolved, it will never resolved by a Promise Object but a static value;
x.then(resolve, reject)
}
return
}
if ((x !== null) && ((typeof x === 'object') || (typeof x === 'function'))) {
try {
then = x.then //because x.then could be a getter
if (typeof then === 'function') {
then.call(x, function rs(y) {
if (thenCalledOrThrow) return
thenCalledOrThrow = true
return resolvePromise(promise2, y, resolve, reject)
}, function rj(r) {
if (thenCalledOrThrow) return
thenCalledOrThrow = true
return reject(r)
})
} else {
resolve(x)
}
} catch (e) {
if (thenCalledOrThrow) return
thenCalledOrThrow = true
return reject(e)
}
} else {
resolve(x)
}
}
Promise.prototype.then = function(onResolved, onRejected) {
var self = this
var promise2
onResolved = typeof onResolved === 'function' ? onResolved : function(v) {
return v
}
onRejected = typeof onRejected === 'function' ? onRejected : function(r) {
throw r
}
if (self.status === 'resolved') {
return promise2 = new Promise(function(resolve, reject) {
setTimeout(function() { // 异步执行onResolved
try {
var x = onResolved(self.data)
resolvePromise(promise2, x, resolve, reject)
} catch (reason) {
reject(reason)
}
})
})
}
if (self.status === 'rejected') {
return promise2 = new Promise(function(resolve, reject) {
setTimeout(function() { // 异步执行onRejected
try {
var x = onRejected(self.data)
resolvePromise(promise2, x, resolve, reject)
} catch (reason) {
reject(reason)
}
})
})
}
if (self.status === 'pending') {
// 这里之所以没有异步执行,是因为这些函数必然会被resolve或reject调用,而resolve或reject函数里的内容已是异步执行,构造函数里的定义
return promise2 = new Promise(function(resolve, reject) {
self.onResolvedCallback.push(function(value) {
try {
var x = onResolved(value)
resolvePromise(promise2, x, resolve, reject)
} catch (r) {
reject(r)
}
})
self.onRejectedCallback.push(function(reason) {
try {
var x = onRejected(reason)
resolvePromise(promise2, x, resolve, reject)
} catch (r) {
reject(r)
}
})
})
}
}
Promise.prototype.catch = function(onRejected) {
return this.then(null, onRejected)
}
Promise.deferred = Promise.defer = function() {
var dfd = {}
dfd.promise = new Promise(function(resolve, reject) {
dfd.resolve = resolve
dfd.reject = reject
})
return dfd
}
本文没有写好,有点头晕,所以具体的实现过程还是建议看下面的参考文章。
参考: