前言
来新公司也有三周了,也逐渐适应了新氛围。这几天把面试时一个答得一塌糊涂的问题做了深入的研究:如何用es5手写Promise?今天做一下详细整理。
正文
分析Promise
Promise能够极大的方便我们管理异步操作。
- 因为es5没有class这个概念,所以Promise是一个构造函数,并且它在创建时立即执行,它接收一个函数作为参数,这个函数一般会有 两个参数resolve和reject,这两个参数其实是两个函数,它们都是使用者调用的,其中resolve在异步操作成功时调用,而reject会在异步操作失败时调用;
- Promise有三种状态
pending
(进行中),fulfilled
(成功)和rejected
(失败),Promise状态只能由pending
变成fulfilled
或者rejected
,并且状态改变不可逆; - then方法,接收两个回调函数作为参数,它们是Promise回调给使用者的,第一个为成功回调,第二个为失败回调,then方法返回一个Promise;
- catch方法,可以捕获到异步操作的失败回调,同时也可以捕获到then方法内部的异常,返回一个Promise;
- finally方法,无论异步操作结果如何都会执行的操作,返回一个Promise;
- resolve方法,会直接返回一个
fulfilled
状态的Promise; - reject方法,会直接返回一个
reject
状态的Promise; - all方法,接收一个数组作为参数,其中数组的每个成员必须是Promise对象,它返回一个Promise对象,只有数组中所有的Promise都成功时,会进入成功回调,否则会进入失败回调;
- race方法,同样接收一个包含Promise的数组作为参数,它会返回最先执行完的Promise的状态,执行成功就进入成功回调,失败就进入失败回调。
手写
有了以上对于Promise的分析和整理,我们就可以按部就班的开始手写自己的Promise
了。
- 构造函数
function MyPromise(fn) {
this._cbs = []
// 用于保存创建MyPromise对象时,resolve或者reject传进来的值,在then方法中再回传回去
this._result = null
function resolve() {}
function reject() {}
// 创建MyPromise对象时,立即执行传进来的函数,并把内部的resolve和reject作为参数传递给fn
fn(resolve, reject)
}
- 三个状态
function MyPromise(fn) {
this.PENDING_STATE = 'pending'
this.FULFILLED_STATE = 'fulfilled'
this.REJECTED_STATE = 'rejected'
this._state = this.PENDING_STATE
this._cbs = []
this._result = null
function resolve() {}
function reject() {}
fn(resolve, reject)
}
引入状态以后,我们需要完善一下resolve和reject方法
function MyPromise(fn) {
this.PENDING_STATE = 'pending'
this.FULFILLED_STATE = 'fulfilled'
this.REJECTED_STATE = 'rejected'
this._state = this.PENDING_STATE
this._cbs = []
this._result = null
var self = this
// resolve函数只有pending状态才会执行,切换状态后,把传入的value保存,
function resolve(v) {
if (self._state !== self.PENDING_STATE) return
self._result = v
self._state = self.FULFILLED_STATE
setTimeout(function() {
self._cbs.forEach(function(cb) {
cb.onFulfilled()
})
})
}
function reject(r) {
if (self._state !== self.PENDING_STATE) return
self._result = r
self._state = self.REJECTED_STATE
setTimeout(function() {
self._cbs.forEach(function(cb) {
cb.onRejected()
})
})
}
try {
fn(resolve, reject)
} catch(e) {
reject(e)
}
}
- then
MyPromise.prototype.then = function(onFulfilled, onRejected) {
if(typeof onFulfilled !== 'function') {
onFulfilled = function() {}
}
if(typeof onRejected !== 'function') {
onRejected = function() {}
}
var self = this
return new MyPromise(function(resolve, reject) {
function handle(cb) {
try {
var result = cb(self._result)
// 链式调用
if (result instanceof MyPromise) {
result.then(function(v) {
resolve(v)
}, function(r) {
reject(r)
})
} else {
// 如果then内部的onFulfilled或者onRejected返回了一个值,则将这个值继续向下传递
// 即Promise.resolve(1).then(() => 2).then(console.log) 此时打印结果为2
if(self._state === self.FULFILLED_STATE) {
resolve(result || self._result)
} else {
reject(result || self._result)
}
}
} catch(e) {
reject(e)
}
}
// 异步操作没执行完,先保存回调函数,等待执行结果
if (self._state === self.PENDING_STATE) {
self._cbs.push({
onFulfilled: function() {
handle(onFulfilled)
},
onRejected: function() {
handle(onRejected)
}
})
}
if (self._state === self.FULFILLED_STATE) {
handle(onFulfilled)
}
if (self._state === self.REJECTED_STATE) {
handle(onRejected)
}
})
}
- catch
MyPromise.prototype.catch = function(onRejected) {
return this.then(undefined, onRejected)
}
- finally
MyPromise.prototype.finally = function(callback) {
return this.then(callback, callback)
}
- resolve
MyPromise.resolve = function(value) {
return new MyPromise(function(resolve, reject) {
if(value instanceof MyPromise) {
value.then(function(v) {
resolve(v)
}, function(r) {
reject(r)
})
} else {
resolve(value)
}
})
}
- reject
MyPromise.reject = function(r) {
return new MyPromise(function(resolve, reject) {
reject(r)
})
}
- all
MyPromise.all = function(list) {
var count = 0
var results = []
return new MyPromise(function(resolve, reject) {
list.forEach(function(myPromise, index) {
myPromise.then(function(v) {
count++
results[index] = v
if(count === list.length) {
resolve(results)
}
}, function(r) {
reject(r)
})
})
})
}
- race
MyPromise.race = function(list) {
return new MyPromise(function(resolve, reject) {
list.forEach(function(myPromise) {
myPromise.then(function(v) {
resolve(v)
}, function(r) {
reject(r)
})
})
})
}
总结
在整理这些东西之前,还以为自己对Promise掌握的还行,结果细究起来还有不少的盲点,做整理的过程会加深自己的理解和记忆,也方便日后查看。
网友评论