promise
实现一个符合promises/A+规范的promise 规范:https://promisesaplus.com/
测试
引入chai和sinon进行测试。启动测试:
yarn test
使用步骤
- 点击 Fork
- 完善 promise-test 里的 src/promist.ts 代码
- 上传代码,点击你的仓库页面的 create pull request 按钮
- Travis CI 会检查你的代码是否正确
- 如果测试成功,你会看到绿色的成功提示
- 如果测试失败,你会看到失败提示
具体实现思路
手写Promise是前端面试中常考的一道题目,本章将和大家一起完成一个符合Promises/A+标准的Promise方法,希望对你有所帮助。
Promise 简介
- 解决什么问题
- 回调地狱
- 如何解决的 - how
- 什么优点(对比其他技术)
- 减少缩进
- 消灭if(err)
- 缺点
- 如何解决这些缺点
Promises/A+标准
Promise 的完整API是什么
promise是一个类
- js中类是特殊的函数
- 类属性:length(可忽略)
- 类方法:all/...
- 对象属性: then/finally/catch
- 对象内部属性: state = pending/fulfilled/rejected
实现promise基本方法
promise是一个类,只接受函数fn,并立即执行该函数
class Promise2 {
constructor(fn) {
if (typeof fn !== "function") {
throw new Error("只接受函数")
}
fn()
}
}
fn执行的时候接受resolve和reject两个函数
class Promise2 {
constructor(fn) {
if (typeof fn !== "function") {
throw new Error("只接受函数")
}
/*********************************/
fn(() => {}, () => {})
/*********************************/
}
}
Promise有then方法
promise.then(succeed,fail) 中的succeed 会在resolve被调用的时候执行,fail会在reject被调用的时候执行
class Promise2 {
succeed = null
fail = null
constructor(fn) {
if (typeof fn !== "function") {
throw new Error("只接受函数")
}
/*********************************/
fn(() => {
// 如果不加setTimeout,此时this.succeed 还是 null
setTimeout(() => {
this.succeed()
})
}, () => {
setTimeout(() => {
this.fail()
})
})
}
then (succeed, fail) {
this.succeed = succeed
this.fail = fail
}
/*********************************/
}
将resolve和reject方法提取出来
class Promise2 {
succeed = null
fail = null
constructor(fn) {
if (typeof fn !== "function") {
throw new Error("只接受函数")
}
/*
* 在内部调用,this.resolve是函数
* this.resolve() 相当于 undefined.this.resolve().call(undefined)
*
*/
fn(this.resolve.bind(this), this.reject.bind(this))
}
resolve () {
setTimeout(() => {
this.succeed()
})
}
reject () {
setTimeout(() => {
this.fail()
})
}
then(succeed, fail) {
this.succeed = succeed
this.fail = fail
}
}
开始根据规范写代码
2.2.1 onFulfilled和onRejected都是可选的参数:(注:onFulfilled为succeed函数,onRejected为fail函数)
2.2.1.1 如果 onFulfilled不是函数,必须忽略
2.2.1.1 如果 onRejected不是函数,必须忽略
class Promise2 {
resolve () {
setTimeout(() => {
/********************************************/
if (typeof this.succeed === 'function') {
this.succeed()
}
/********************************************/
})
}
reject () {
setTimeout(() => {
if (typeof this.fail === 'function') {
this.fail()
}
})
}
then(succeed?, fail?) {
this.succeed = succeed
this.fail = fail
}
}
2.2.2 如果onFulfilled是函数:(2.2.3 onRejected同理)
2.2.2.1 此函数必须在promise 完成(fulfilled)后被调用,并把promise 的值作为它的第一个参数
2.2.2.2 此函数在promise完成(fulfilled)之前绝对不能被调用
2.2.2.2 此函数绝对不能被调用超过一次
class Promise2 {
succeed = null
fail = null
/****************************************/
// 加入三个状态
state = 'pending'
resolve (result) {
setTimeout(() => {
/* 2.2.2.2 此函数绝对不能被调用超过一次 */
if (this.state !== 'pending') {return}
this.state = 'fulfilled'
if (typeof this.succeed === 'function') {
// 2.2.2.1 promise 的值作为它的第一个参数
this.succeed(result)
}
/****************************************/
})
}
reject (reason) {
setTimeout(() => {
if (this.state !== 'pending') {return}
this.state = 'rejected'
if (typeof this.fail === 'function') {
this.fail(reason)
}
})
}
}
2.2.4 在你的代码没有执行完毕之前,不得调用then后面的两个函数
目前的代码符合这条标准,因为then后的函数都是放在setTimeout中执行的。
2.2.5 onFulfilled 和 onRejected 必须被当做函数调用(eg.不能有this传递过来)
call 就完了
// ...
if (typeof this.succeed === 'function') {
this.succeed.call(undefined, result)
}
// ...
2.2.6 then可以在同一个promise里被多次调用
2.2.6.1 如果/当 promise 完成执行(fulfilled),各个相应的onFulfilled回调
必须根据最原始的then 顺序来调用
2.2.6.2 如果/当 promise 被拒绝(rejected),各个相应的onRejected回调
必须根据最原始的then 顺序来调用
class Promise2 {
state = 'pending'
/****************************************/
// 将函数放入数组中,保存下来
callbacks = []
/****************************************/
constructor(fn) {
if (typeof fn !== "function") {
throw new Error("只接受函数")
}
fn(this.resolve.bind(this), this.reject.bind(this))
}
resolve (result) {
setTimeout(() => {
if (this.state !== 'pending') {return}
this.state = 'fulfilled'
/****************************************/
// 依次调用
this.callbacks.forEach(handle => {
if (typeof handle[0] === 'function') {
handle[0].call(undefined, result)
}
})
/****************************************/
})
}
reject (reason) {
setTimeout(() => {
if (this.state !== 'pending') {return}
this.state = 'rejected'
this.callbacks.forEach(handle => {
if (typeof handle[1] === 'function') {
handle[1].call(undefined, reason)
}
})
})
}
then(succeed?, fail?) {
/****************************************/
this.callbacks.push([succeed, fail])
/****************************************/
}
}
2.2.7 then必须返回一个promise
promise2 = promise1.then(onFulfilled, onRejected);
2.2.7.1 如果onFulfilled或onRejected返回一个值x, 运行 Promise Resolution Procedure [[Resolve]](promise2, x) 2.2.7.2 如果onFulfilled或onRejected抛出一个异常e,promise2 必须被拒绝(rejected)并把e当作原因 2.2.7.3 如果onFulfilled不是一个方法,并且promise1已经完成(fulfilled), promise2必须使用与promise1相同的值来完成(fulfiled) 2.2.7.4 如果onRejected不是一个方法,并且promise1已经被拒绝(rejected), promise2必须使用与promise1相同的原因来拒绝(rejected)
class Promise2 {
state = 'pending'
callbacks = []
constructor(fn) {
if (typeof fn !== "function") {
throw new Error("只接受函数")
}
fn(this.resolve.bind(this), this.reject.bind(this))
}
resolve (result) {
process.nextTick(() => {
if (this.state !== 'pending') {return}
this.state = 'fulfilled'
this.callbacks.forEach(handle => {
if (typeof handle[0] === 'function') {
/**********************************************/
const x = handle[0].call(undefined, result)
handle[2].resolveWith(x)
/**********************************************/
}
})
})
}
reject (reason) {
process.nextTick(() => {
if (this.state !== 'pending') {return}
this.state = 'rejected'
this.callbacks.forEach(handle => {
if (typeof handle[1] === 'function') {
/**********************************************/
const x = handle[1].call(undefined, reason)
handle[2].resolveWith(x)
/**********************************************/
}
})
})
}
then (succeed?, fail?) {
const handle = []
handle[0] = succeed
handle[1] = fail
handle[2] = new Promise2(() => {})
this.callbacks.push(handle)
return handle[2]
}
/**********************************************/
resolveWith (x) {
if (this === x) {
return this.reject(new TypeError())
}
else if (x instanceof Promise2) {
x.then((result) => {
this.resolve(result)
}, reason => {
this.reject(reason)
})
}
else if (x instanceof Object) {
let then
try {
then = x.then
} catch (e) {
this.reject(e)
}
if (then instanceof Function) {
try {
x.then(y => {
this.resolveWith(y)
}, r => {
this.reject(r)
})
} catch (e) {
this.reject(e)
}
}else {
this.resolve(x)
}
} else {
this.resolve(x)
}
}
/**********************************************/
}
export default Promise2
重构
- 将 resolve 和 reject抽取为一个函数 resolveOrReject
- 从 resolveWith 中抽取三个方法resolveWithSelf、resolveWithPromise、resolveWithObject
class Promise2 {
state = 'pending'
callbacks = []
constructor(fn) {
if (typeof fn !== "function") {
throw new Error("只接受函数")
}
fn(this.resolve.bind(this), this.reject.bind(this))
}
resolve (result) {
this.resolveOrReject('fulfilled', result, 0)
}
reject (reason) {
this.resolveOrReject('rejected', reason, 1)
}
resolveOrReject (state, data, i) {
process.nextTick(() => {
if (this.state !== 'pending') {return}
this.state = state
this.callbacks.forEach(handle => {
if (typeof handle[i] === 'function') {
let x = handle[i].call(undefined, data)
handle[2].resolveWith(x)
}
})
})
}
then (succeed?, fail?) {
const handle = []
handle[0] = succeed
handle[1] = fail
handle[2] = new Promise2(() => {})
this.callbacks.push(handle)
return handle[2]
}
resolveWithSelf () {
return this.reject(new TypeError())
}
resolveWithPromise (x) {
x.then(result => {this.resolve(result)}, reason => {this.reject(reason)})
}
resolveWithObject (x) {
let then
try { then = x.then } catch (e) { this.reject(e) }
if (then instanceof Function) {
try { x.then(y => { this.resolveWith(y) }, r => { this.reject(r) }) }
catch (e) { this.reject(e) }
}
else { this.resolve(x) }
}
resolveWith (x) {
if (this === x) {
this.resolveWithSelf();
}
else if (x instanceof Promise2) {
this.resolveWithPromise(x);
}
else if (x instanceof Object) {
this.resolveWithObject(x);
}
else { this.resolve(x) }
}
}