手撸 Promise
- Promise作为ES6的核心内容,是前端童鞋必备的基础知识!更是面试通关的必刷题!
- Promise的出现,解决了"蛮荒时代"的回调地狱,让js异步callback走向简洁,优雅!
- 本文参照Promise/A+实现(不足之处,请留言指出)
第一版(初出茅庐)
状态
- Promise有三种状态值标识当前的异步状态!
- pending状态可变为fulfilled 或 rejected
- 一旦处于fulfilled 或 rejected时 则不可改为其他状态
const PENDING = 'pending' // 待定,等待
const FULFILLED = 'fulfilled' // 成功
const REJECTED = 'rejected' // 失败
promise的基本实现
- promise构造函数、resolve方法、reject方法、then方法
// promise构造函数
function Promise(executor) {
this.state =PENDING
this.value = undefined // 当状态fulfilled,给定一个value值
this.reason = undefined // 当状态rejected,给定一个失败原因
// resolve方法
function resolve(value) {
if (this.state === PENDING) {
this.state = FULFILLED
this.value = value
}
}
// reject方法
function reject(reason) {
if (this.state === PENDING) {
this.state = REJECTED
this.reason = reason
}
}
try {
executor(resolve, reject)
} catch (e) {
reject(e)
}
}
- then方法
当状态为fulfilled时调用onFulfilled, 当状态为rejected时调用onRejected
Promise.prototype.then = function(onFulfilled, onRejected) {
if(this.state === FULFILLED){
onFulfilled(this.value)
}
if(this.state === FULFILLED){
onFulfilled(this.reason)
}
}
此时基本有了promise的样子,但Promise真正要解决的异步问题,并未处理!
第二版 克敌(异步)制胜
- 头脑(浆糊)风暴,思考then方法是同步调用,那必然会在异步任务完成前执行,该如何处理呢?
此时次刻,聪明的你必然想到了订阅发布,先通过then订阅异步成功后要执行的任务,在resolve 或 reject调用后执行已订阅的任务
// 给Promise中添加两个事件池,存储订阅事件
function Promise(executor) {
......
this.onFulfilledCallBack = [] // onFulfilled订阅池
this.onRejectedCallBack = [] // onRejected订阅池
......
// resolve方法、reject方法
function resolve(value) {
if (self.state === PENDING) {
self.state = FULFILLED
self.onFulfilledCallBack.forEach(cb => cb(self.reason))
}
}
function reject(reason) {
if (self.state === PENDING) {
self.state = REJECTED
self.reason = reason
self.onRejectedCallBack.forEach(cb => cb(self.reason))
}
}
// 将onFulfilled, onRejected 放入事件池中
Promise.prototype.then = function(onFulfilled, onRejected) {
if(this.state === FULFILLED){
onFulfilled(this.value)
}
if(this.state === FULFILLED){
onFulfilled(this.reason)
}
if(this.state === PENDING){
this.onFulfilledCallBack.push(onFulfilled)
this.onRejectedCallBack.push(onRejected)
}
}
此时此刻,恭喜你正式成为一名江湖(江湖)剑客,可以行侠仗义了
带三版 千秋万代一统江湖-then方法的链式调用
- 根据Promise/A+规范,then方法返回一个新的promise
Promise.prototype.then = function(onFulfilled, onRejected) {
let promise2 = new Promise((resolve, reject) => {
if (this.state === FULFILLED) {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
};
if (this.state === REJECTED) {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
};
if (this.state === PENDING) {
this.onFulfilledCallBack.push(()=>{
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
})
this.onRejectedCallBack.push(()=>{
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
})
}
})
return promise2
}
- 注意 resolvePromise 方法,我在调用事件池里的方法后,为什么要resolvePromise方法将新的promise2与回调方法的返回值通过resolvePromise处理呢???
- 思考:若事件池内的方法(then中用户传入的回调方法),返回的也是一个promise我们处理逻辑又将如何执行
- 根据示例:观察原生Promise
let test = new Promise((resolve, reject) => {
setTimeout(() => {
console.log(1)
resolve(1)
}, 1000)
})
test
.then(data => {
console.log('data:', data)
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(2)
resolve(2)
}, 2000)
}).then(data => {
console.log('data:', data)
console.log(3)
return 3
})
})
.then(data => {
console.log('data:', data)
console.log(4)
})
// 输出结果
1
data: 1
2
data: 2
3
data: 3
4
- 据此我们可以发现外层的then回调会在内层的promise的then回调全部执行完才会执行,那内外层的promise必然有着不可告人的秘密,他们就是通过resolvePromise方法“勾结”在一起
resolvePromise方法
- 参数有promise2(then默认返回的promise)、x(then回调中return的对象)、resolve、reject
// 这里先给出完整的 resolvePromise 代码,看完后我们来思考几个问题
function resolvePromise(promise2, x, resolve, reject){
// 处理循环引用
if(x === promise2){
// reject报错
return reject(new TypeError('Chaining cycle detected for promise'));
}
// 防止多次调用
let called;
if (x instanceof Promise) {
if (x.state === PENDING) {
x.then(
y => {
resolvePromise(promise2, y, resolve, reject)
},
reason => {
reject(reason)
}
)
} else {
x.then(resolve, reject)
}
} else if (x != null && (typeof x === 'object' || typeof x === 'function')) {
try {
// A+规定,声明then = x的then方法
let then = x.then;
// 如果then是函数,就默认是promise了
if (typeof then === 'function') {
// 就让then执行 第一个参数是this 后面是成功的回调 和 失败的回调
then.call(x, y => {
// 成功和失败只能调用一个
if (called) return;
called = true;
// resolve的结果依旧是promise 那就继续解析
resolvePromise(promise2, y, resolve, reject);
}, err => {
// 成功和失败只能调用一个
if (called) return;
called = true;
reject(err);// 失败了就失败了
})
} else {
// 返回非promise,内层promise完成,执行promise2的resolve 回归到外城then的调用
resolve(x); // 直接成功即可
}
} catch (e) {
if (called) return;
called = true;
reject(e);
}
} else {
resolve(x);
}
}
- 思考1: x === promise2,看下例子
let p1 = new Promise(resolve => {
resolve(1);
});
var p2 = p1.then(data => {
return p2;
})
如果不对x === promise2处理,会导致循环引用,自己等待自己
- 思考2: else if (x != null && (typeof x === 'object' || typeof x === 'function'))
兼容符合 Promise/A+ 的 then 方法, 使实现方式不同(只要符合规范)的promise能够相互调用。
第五版 内外兼修 处理异常
- then中onFulfilled, onRejected均为非必填参数
- onFulfilled, onRejected均为异步调用
- onFulfilled, onRejected执行时用try catch捕获异常然后reject(err)
Promise.prototype.then = function(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
// onRejected 若未传,则通过throw err的方式将err抛出
onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };
let promise2 = new Promise((resolve, reject) => {
if (this.state === FULFILLED) {
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0)
};
if (this.state === REJECTED) {
setTimeout(() => {
// 如果报错
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
};
if (this.state === PENDING) {
this.onFulfilledCallBack.push(()=>{
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
})
this.onRejectedCallBack.push(()=>{
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
})
}
})
return promise2
}
- 到此你已完成了一个promise
完整代码
// state 3种状态
const PENDING = 'pending' // 待定
const FULFILLED = 'fulfilled' // 成功
const REJECTED = 'rejected' // 失败
function Promise(executor) {
this.state = PENDING
this.value = undefined
this.reason = undefined
this.onFulfilledCallBack = []
this.onRejectedCallBack = []
let resolve = value => {
if (this.state === PENDING) {
this.state = FULFILLED
this.value = value
this.onFulfilledCallBack.forEach(fn => fn())
}
}
let reject = reason => {
if (this.state === PENDING) {
this.state = REJECTED
this.reason = reason
this.onRejectedCallBack.forEach(fn => fn())
}
}
try {
executor(resolve, reject)
} catch (err) {
reject(err)
}
}
Promise.prototype.then = function(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
// onRejected 若未传,则通过throw err的方式将err抛出
onRejected =
typeof onRejected === 'function'
? onRejected
: err => {
throw err
}
let promise2 = new Promise((resolve, reject) => {
if (this.state === FULFILLED) {
setTimeout(() => {
try {
let x = onFulfilled(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
}
if (this.state === REJECTED) {
setTimeout(() => {
// 如果报错
try {
let x = onRejected(this.reason)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
}
if (this.state === PENDING) {
this.onFulfilledCallBack.push(() => {
setTimeout(() => {
try {
let x = onFulfilled(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
})
this.onRejectedCallBack.push(() => {
setTimeout(() => {
try {
let x = onRejected(this.reason)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
})
}
})
return promise2
}
function resolvePromise(promise2, x, resolve, reject) {
// 处理循环引用
if (x === promise2) {
// reject报错
return reject(new TypeError('Chaining cycle detected for promise'))
}
// 防止多次调用
let called
if (x instanceof Promise) {
if (x.status === PENDING) {
x.then(
y => {
resolvePromise(promise2, y, resolve, reject)
},
reason => {
reject(reason)
}
)
} else {
x.then(resolve, reject)
}
} else if (x != null && (typeof x === 'object' || typeof x === 'function')) {
try {
// A+规定,声明then = x的then方法
let then = x.then
// 如果then是函数,就认为是promise
if (typeof then === 'function') {
// then执行,降this指向x
then.call(
x,
y => {
// 成功和失败只能调用一个
if (called) return
called = true
// resolve的结果依旧是promise 那就继续解析
resolvePromise(promise2, y, resolve, reject)
},
err => {
// 成功和失败只能调用一个
if (called) return
called = true
reject(err) // 失败了就失败了
}
)
} else {
// 返回非promise,内层promise完成,执行promise2的resolve 回归到外城then的调用
resolve(x) // 直接成功即可
}
} catch (e) {
if (called) return
called = true
reject(e)
}
} else {
resolve(x)
}
}