blog
blog copied to clipboard
如何实现一个简单Promise
Promise
表示一个异步操作的最终结果,和Promise
最主要的交互方式方法是通过传入它的then
方法从而获取Promise
最终的值或Promise
最终拒绝的原因。
Promise 声明
首先,Promise肯定是一个类
- promise是这么用的!
new Promise((resolve, reject) => {})
,所以这个参数(也就是函数)可以叫executor
,传进去就执行 - executor里面有两个参数,一个叫resolve(成功),一个叫reject(失败)
- 两个参数都是可以执行的,所以要先声明一下
class Promise {
constructor (executor) {
let resolve = () => {}
let reject = () => {}
executor(resolve, reject)
}
}
Promise 状态
一个 Promise
必须只能处于一个状态中: pending, fulfilled, rejected
- 如果是
pending
状态,则Promise
:- 可以转换到
fulfilled
或rejected
状态 - 如果转化成
fulfilled
以后不可转为其他状态,并且必须要有一个不可以改变的值(value),通过resolve(value)
- 如果转化成
rejected
以后也不可以转为其他状态,并且必须要有一个不可一改变的原因(reason),通过reject(reason)
- 可以转换到
- 如果是
fulfilled
状态或者fulfilled
,则Promise
:- 不可以转换成任何其他状态
- 必须有一个值,并且这个值不能改变
- 如果传入执行的函数报错了,那么直接
reject()
所以以上的描述我们可以怎么做?看下面
class Promise {
this.state = 'pending' // 设置初始时的状态
this.value = undefined // 成功值
this.reason = undefined // 失败原因
let resolve = value => {
if (this.state === 'pending') {
this.state = 'fulfilled' // 改变成为成功状态
this.value = value // 存储成功值
}
}
let reject = reason => {
if (this.state === 'pending') {
this.state = 'rejected' // 改变为失败状态
this.reason = reason // 存储失败原因
}
}
try {
executor(resolve, reject)
} catch (error) {
reject(error)
}
}
then 方法
Promise
有一个叫做 then 的方法,里面有两个参数: onFulfilled, onRejected,成功的时候有成功的值,失败也有失败的原因
- 如果状态是
fulfilled
,那么执行 onFulfilled,传入 this.value。如果是rejected
,那么执行 onRejected,传入 this.reason - 这两个参数假设他们是函数的话,那么必须在
fulfilled
和rejected
后被调用,value 和 reason 依次作为他们的第一个参数
class Promise {
// 这里代表两个参数
then (onFulfilled, onRejected) {
if (this.state === 'fulfilled') {
// 如果状态变成 fulfilled,执行 onFulfilled,并且传入成功的值
onFulfilled(this.value)
}
if (this.state === 'rejected') {
onRejected(this.reason)
}
}
}
异步问题
上面的代码等价于一个简单的同步代码,但是如果 resolve
在 setTimeout
内执行 then
时 state
的状态还是 pending
,这样就会有问题,所以需要把无论成功与否的成功和失败的数据放到分别的数组中,然后在 reject
或者 resolve
的时候再去循环调用就可以了
class Promise {
constructor (executor) {
this.onResolvedCallbacks = [] // 成功值存放的数组
this.onRejectedCallbacks = [] // 失败值存放的数组
}
let resolve => this.onResolvedCallbacks.forEach(fn => fn())
let reject => this.onRejectedCallbacks.forEach(fn => fn())
then (onFulfilled, onRejected) {
if (this.state === 'pending') {
this.onResolvedCallbacks.push(() => onFulfilled(this.value)) // onFulfilled传入到成功数组
this.onRejectedCallbacks.push(() => onRejected(this.reason)) // onRejected传入到失败数组
}
}
}
解决链式调用
简单说就是类似 new Promise().then().then()
这样
- 既然要达成链式,那么就要在
then
里面返回一个新的promise
,我们可以称成promise2
- 把这个
promise2
返回给下一个promise
- 如果返回的是一个普通的值,那么直接吧普通的值直接传给下一个
then
就好了
- 如果在第一个
then
中 return了一个参数(未知参数),那么return
出来的新的promise
就是onFulfilled()
或者onRejected()
的值,这个返回的值称为x
,判断x
的函数叫做resolvePromise
- 判断x是不是
promise
- 如果是的话拿到结果,作为新的
promise2
成功的结果 - 如果是普通值,直接作为
promise2
成功的结果 - 然后比较
promise
和x
-
resolvePromise
的参数有promise2
(默认返回的promise)、x
(我们自己 return 的对象)、resolve
、reject
-
resolve
和rejected
是promise2
的
class Promise {
then (onFulfiled, onRejected) {
// 声明返回的 promise2
let promise2 = new Promise((resolve, reject) => {
if (this.state === 'fulfilled') {
let x = onFulfiled(this.value)
resolvePromise(promise2, x, resolve, reject) // 这里就是处理自己return的promise和默认的promise2的关系
}
if (this.state === 'rejected') {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject)
}
if (this.state === 'pending') {
this.onResolvedCallbacks.push(() => {
let x = onFulfilled(this.value)
resolvePromise(promise2, x, resolve, reject)
})
this.onRejectedCallbacks.push(() => {
let x = onRejected(this.reason)
resolvePromise(promise2, x, resolve, reject)
})
}
})
return promise2 // 返回promise,达成链式
}
}
resolvePromise函数是什么
让不同的promise代码互相套用,叫做resolvePromise
- 当
x === promise2
的时候就会造成循环引用,相当于自己等待自己完成,就会报错
let p = new Promise(resolve => {
resolve(0)
})
var p2 = p.then(d => {
return p2 // 自己wait自己,永无止境
})
所以需要判断 x
不能是 null
并且是对象或者函数(包括promise)
function resolvePromise(promise2, x, resolve, reject) {
// 解决循环引用报错问题
if (x === promise2) {
return reject(new TypeError('error'))
}
let called // 调用标示,防止多次调用
if (x != null && (typeof x === 'object' || typeof x === 'function')) {
try {
let then = x.then
if (typeof then === 'function') { // 判断自己return的是不是function
/**
这里call中,第一个参数是this,后面两个分别是成功和失败的callback
并且成功和失败只能成功一个
*/
then.call(x, y => {
if (called) return
resolvePromise(promise2, y, resolve, reject)
}, err => {
if (called) return
called = true
reject(err)
})
} else {
resolve(x) // 直接成功
}
} catch (err) {
if (called) return
called = true
reject(err)
}
} else {
resolve(x) // x是普通值
}
}
琐碎问题
-
onFulfilled
,onRejected
都是可选参数,如果他们不是函数,必须被忽略
-
onFulfilled
返回一个普通值直接返回 -
onRejected
返回一个普通值,那么相当于value => value
,就会跑到下一个then
中的onFulfilled
中,所以直接抛错即可
-
onFulfilled
,onRejected
不能同步调用,必须异步,所以用setTimeout
来解决
-
onFulfilled
,onRejected
如果报错的话,直接返回reject()
即可
完整代码如下
class Promise {
constructor (executor) {
this.state = 'pending'
this.value = undefined
this.reason = undefined
this.onResolvedCallbacks = []
this.onRejectedCallbacks = []
let resolve = value => {
if (this.state === 'pending') {
this.state = 'fulfilled'
this.value = value
this.onResolvedCallbacks.forEach(fn => fn())
}
}
let reject = reason => {
if (this.state === 'pending') {
this.state = 'rejected'
this.reason = reason
this.onRejectedCallbacks.forEach(fn => fn())
}
}
try {
executor(resolve, reject)
} catch (err) {
reject(err)
}
}
then (onFulfilled, onRejected) {
// 判断 onFulfilled 和 onRejected 是否函数
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
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.onResolvedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onFulfilled(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
})
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onRejected(this.reason)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
})
}
})
// 返回promise,完成链式
return promise2
}
catch (fn) {
return this.then(null,fn)
}
}
function resolvePromise (promise2, x, resolve, reject) {
if (x === promise2) {
return reject(new TypeError('err'))
}
let called
if (x != null && (typeof x === 'object' || typeof x === 'function')) {
try {
let then = x.then
if (typeof then === 'function') {
then.call(x, y => {
if (called) return
called = true
resolvePromise(promise2, y, resolve, reject)
}, err => {
if (called) return
called = true
reject(err)
})
} else {
resolve(x)
}
} catch (e) {
if (called) return
called = true
reject(e)
}
} else {
resolve(x)
}
}
验证
首先在 Promise
后加上如下代码
Promise.defer = Promise.deferred = function () {
let dfd = {}
dfd.promise = new Promise((resolve,reject)=>{
dfd.resolve = resolve;
dfd.reject = reject;
});
return dfd;
}
module.exports = Promise
npm i promises-aplus-tests -g
promises-aplus-tests [文件名字]即可
拓展ES5方式
function testPromise (fn) {
let self = this
this.status = 'pending'
this.data = undefined
this.handleResolvedCallback = []
this.handleRejectedCallback = []
function resolve (value) {
setTimeout(function () {
if (self.status === 'pending') {
self.status = 'resolved'
self.data = value
for (let i = 0; i < self.handleResolvedCallback.length; i++) {
self.handleResolvedCallback[i](value)
}
}
}, 0)
}
function reject (err) {
setTimeout(function () {
if (self.status === 'pending') {
self.status = 'rejected'
self.data = err
for (let i = 0; i < self.handleRejectedCallback.length; i++) {
self.handleRejectedCallback[i](err)
}
}
}, 0)
}
try {
fn(resolve, reject)
} catch (e) {
reject(e)
}
}
testPromise.prototype.then = function (onResolved, onRejected) {
let self = this
let promise2 = null
onResolved = typeof onResolved === 'function' ? onResolved : function (val) { return val }
onRejected = typeof onRejected === 'function' ? onRejected : function (err) { return err }
if (self.status === 'resolved') {
return promise2 = new testPromise(function (resolve, reject) {
setTimeout(function () {
try {
let b = onResolved(self.data)
if (b instanceof testPromise) {
b.then(resolve, reject)
} else {
resolve(b)
}
} catch (e) {
reject(e)
}
}, 0)
})
}
if (self.status === 'rejected') {
return promise2 = new testPromise(function (resolve, reject) {
setTimeout(function () {
try {
let b = onRejected(self.data)
if (b instanceof testPromise) {
b.then(resolve, reject)
}
} catch (e) {
reject(e)
}
}, 0)
})
}
if (self.status === 'pending') {
return promise2 = new testPromise(function (resolve, reject) {
self.handleResolvedCallback.push(function (value) {
try {
let b = onResolved(self.data)
if (b instanceof testPromise) {
b.then(resolve, reject)
}
} catch (e) {
reject(e)
}
})
self.handleResolvedCallback.push(function (err) {
try {
let b = onRejected(self.data)
if (b instanceof testPromise) {
b.then(resolve, reject)
}
} catch (e) {
reject(e)
}
})
})
}
}
testPromise.prototype.catch = function (onRejected) {
return this.then(null, onRejected)
}
拓展ES6 方式
class testPromise {
constructor(fn) {
this.status = 'pending'
this.value = null
this.fnArr = { resolved: [], rejected: [] }
let handle = (status, value) => {
if (this.status === 'pending') {
this.status = status
this.value = value
this.fnArr[status].forEach(fn => {
fn.call(this, status)
})
}
}
let resolve = handle.bind(this, 'resolved')
let reject = handle.bind(this, 'rejected')
fn(resolve, reject)
}
then(resFn, rejFn) {
if (!isFn(resFn)) resFn = (val) => val
if (!isFn(rejFn)) rejFn = (err) => err
return new testPromise((resolve, reject) => {
let resolveFn = (val) => {
setTimeout(() => {
try {
let v = resFn(val)
if (isThen(v)) {
v.then(resolve, reject)
} else {
resolve(v)
}
} catch(e) {
reject(e)
}
}, 0)
}
let rejectFn = (err) => {
try {
let e = rejFn (err)
if (isThen(e)) {
e.then(resolve, reject)
}
} catch(e) {
reject(e)
}
}
switch (this.status) {
case 'pending':
this.fnArr['resolved'].push(resolveFn)
this.fnArr['rejected'].push(rejectFn)
break
case 'resolved':
resolveFn(this.value)
break
case 'rejected':
rejectFn(this.value)
break
}
})
}
}
const isFn = (fn) => {
return typeof fn === 'function'
}
const isThen = (val) => {
return val && this.isFn(val.then)
}