共计 7684 个字符,预计需要花费 20 分钟才能阅读完成。
手撸 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 = null // 当状态 fulfilled,给定一个 value 值
this.reason = null // 当状态 rejected,给定一个失败原因
// resolve 方法
function resolve(value) {if (this.status === PENDING) {
this.status = FULFILLED
this.value = value
}
}
// reject 方法
function reject(reason) {if (this.status === PENDING) {
this.status = 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.status === PENDING) {
self.status = FULFILLED
self.onFulfilledCallBack.forEach(cb => cb(self.reason))
}
}
function reject(reason) {if (self.status === PENDING) {
self.status = 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.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 后面是成功的回调 和 失败的回调
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 后面是成功的回调 和 失败的回调
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)
}
}
正文完
发表至: javascript
2019-10-31