🔍重点:为什么需要一个数组来存放onFulfilled这个callback --> .then可以被多次执行 可以注册多个回调,状态改变的时候,多个回调会依次按照注册的顺序来执行
一、promiseA+规范梳理
1. Promise states
-
pending 初始状态
-
fulfilled / rejected 最终态,promise被resolve/reject后改变状态,且必须有value/resaon值
👉 【必须有value】 那么平时经常写的resolve() 实际相等于resolve了一个undefined。value可以是各种类型 undefined/boolean甚至是一个promise
1.1 状态流转
pending -> 通过resolve(value)方法
->fullfilled
pending -> 通过reject(reason)方法
->rejected
pending、fulfilled、rejected是状态(动作的结果) ; resolve和reject是动作
2. then
promise.then(onFulfilled, onRejected)
2.1 参数要求
传入的onFulfilled
,onRejected
必须是函数,不是则忽略
2.2 onFulfilled 、onRejected特性
-
promise状态改变的时候 调用相应的回调 (状态变为fulfilled,回调这个
onFulfilled
,参数是value。rejected状态同理) -
状态改变之前回调不应该调用
-
只能被调用一次 即状态改变后执行回调,回调后就不会再被调用了 🚩【实现思路:通过变量限制执行次数】
-
onFulfilled
,onRejected
应该是微任务
2.3 then方法
可以被调用多次,promise状态变为fulfilled时,所有的onFulfilled
都按照then的注册顺序执行;rejected状态同理 按照then中注册的onRejected
顺序执行。举例 👇
🚩【按顺序执行cb的实现思路:各需要一个数组来存储onFulfilled
以及onRejected
的callback】
let promise = new Promise
promise.then(cb1).then(cb2).then(cb3) //按注册顺序先执行cb1再执行cb2再执行cb3…
2.4 返回值
- then的返回值是一个promise ,而且是一个新的promise (⚠️重点牢记)
promise2 = promise1.then(onFulfilled,onRejected)
- 回调函数
onFulfilled
或onRejected
的返回结果 (以下用cb代替数)- 若cb为函数
- 执行结果为x,调用
resolvePromise
- cb
onFulfilled
或onRejected
执行抛出异常时,promise2需要被reject()
- 执行结果为x,调用
- 若cb不为函数,上述说过应被忽略,那如何忽略呢?
-
若
onFulfilled
不是函数 ,那么promise2以promise1的value来触发fulfilled状态 -
若
onRejected
不是函数 promise 2以promise1的reason来触发rejected状态
👉总结: 即cb不是函数 promise1有什么状态 promise2就跟风这个状态,指的是状态跟风 ,值得透传
-
- 若cb为函数
2.5 resolvePromise
resolvePromise 真正解决promise返回结果的 解析promise的执行函数
resolvePromise(promise2,x,resolve,reject)
// promise2:新生成的promise实例
// x :上述cb正常执行完的结果
// resolve,reject :上面传进来的可以更改状态的方法
-
promise2 和 x相等,造成死循环,reject出去并报错
-
如果x是一个promise
-
且x状态为pending,那么新的promise也必须是pending状态,直到x状态变成fulfilled或rejected
-
且x状态为fulfilled,那么直接以x的状态给出去 fulfill the promise with the same value,类似上述的值得透传
-
且x状态为rejected,reject the promise with the same reason
总结:cb不为函数的情况 以及 cb的返回值x的promise状态 都有透传值和状态
-
-
如果x是一个Object或者function ,则
let then = x.then
判断这一步的赋值是否会报错- 如果x.then这一步出错了,假设是一个obj,那么obj.then最多产出undefined 为什么会出错呢?(可能修改了原型链
- 如果then得到的是一个函数,那么就去改变这个then函数的指针,
then.call(x,resolvePromiseFn,rejectPromiseFn)
让当前调用then的this变成了x,所以此处相当于x.then(这样就指向x),并传入then里面所需的2个回调- resolvePromiseFn的入参是y, 执行
resolvePromise(promise2, y, resolve, reject)
- resolvePromiseFn的入参是y, 执行
- 如果调用then 抛出异常e,如果resolvePromiseFn,rejectPromiseFn已被执行完那么就不管了(因为状态改变后它的cb只会被执行一次),如果还没执行就以e为reason reject
二、根据规范手写promise
思路整理:
-
定义promise的3个状态
-
设置初始属性状态
-
实现状态更改的两个函数 reject() resolve()
fulfilled和rejected是最终态,所以但是两者之间是不能有状态流转的,所以resolve()和reject()方法中对于状态的判定 加一层条件 来判断之前过来的是否为pending状态
-
处理promise的入参 即const promise = new Promise(
(resolve,reject)=>{}
)传进来的callback4.1 这个fn接受resolve和reject两个参数
4.2 fn执行时机:初始化promise的时候就要执行这个函数fn,只要报错就要立即reject 所以用try catch包裹。
4.3 为什么通过bind去传值? 这个resolve()和reject在这个promise类中可以通过this去调用。但是传进来的fn如果不是箭头函数,只是一个普通函数,那么直接调用this.resolve的时候可能会发现外界的执行环境this上是没有resolve方法的
const promise = new Promise((resolve, reject) => {
resolve()
})
// Promise里的cb (resolve, reject) => { resolve() }
// 类中接收并调用这个fn ,调用fn的时候传入参数,参数为class中改变状态的2个方法
// this.resolve.bind(this) --传给--> cb中的resolve形参
// this.reject.bind(this) --传给--> cb中的reject形参
// 根据cb函数里执行resolve还是reject参数 来决定执行this.resolve还是this.reject方法
-
then方法
5.1 判断是否返回函数? 是则原样返回onFulfilled函数,不是则透传(接收什么返回什么,接收value返回value),写成函数也是为了接收什么返回什么
5.2 then方法整体返回的是一个promise,比如promise2
5.3 根据promise1不同状态 ,promise2的回调会执行不同的方法
💬 点then的时候就会收集所有的cb,只不过还没调用
比较容易想到的是switch分类 状态为fulfilled/ rejected执行对应回调。 但是还有一种状态是pending 解释:.then在promise实例后会立即注册,但是then里面的回调不会立即调用。 例如:异步的时候才resolve 进去then 这个promise的状态还是pending 所以先用数组把所有cb存起来
5.4 若调用.then的时候promise1 为pending状态
先存起来,当状态变为fulfilled和rejected的时候才去执行then里面的callback。
a. 如果then的时候还是Pending就存入cb数组
b. status变化的时候去执行存入数组中的回调--> set时候更新status,并判断新的status状态,fulfilled的则调用fulfilled存放的数组当状态发生变化的时候去执行什么...这个语义更适合使用
getter()
和setter()
这种拦截触发器
-
then里的cb执行抛出异常,promise2需要被reject() --规范中的第2.4条
抛出异常try/catch
-
cb执行成功 返回值x,则运行
resolvePromise(promise2,x,resolve,reject)
7.1 判断x是否和promise2相等
7.2 判断x是否为promise,是则让新的promise接收x的状态
7.3 判断x是是否为对象or函数
①排除null ②try then=x.then ③then是个函数:一个回调只能执行一次,then.call ;不是,当个值直接resolve回去
7.4 判断x是否为普通值
完整代码
// 1.定义三种状态
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MPromise {
FULFILLED_CALLBACK_LIST = [] //也是一种属性写法。编译结果仍然会把它绑定在this上
REJECTED_CALLBACK_LIST = []
_status = PENDING
constructor(fn) {
//2.设置初始状态
this.status = PENDING
this.value = null
this.reason = null
// 4.处理promise的入参 回调函数
try {
fn(this.reslove.bind(this), this.reject.bind(this))
} catch (error) {
this.reject(error)
}
}
// 5.4 监听状态的变化来执行then里面的回调
get status() {
return this._status
}
set status(newStatus) {
this._status = newStatus //更新状态
//判断新的状态
switch (newStatus) {
case FULFILLED: {
this.FULFILLED_CALLBACK_LIST.forEach((callback) => {
callback(this.value)
})
break
}
case REJECTED: {
this.REJECTED_CALLBACK_LIST.forEach((callback) => {
callback(this.reason)
})
break
}
}
}
// 3.实现状态更改的两个函数 reslove和reject方法
resolve(value) {
if (this.status == PENDING) {
this.value = value //先更新value
this.status = FULFILLED //再更新状态,这样触发set status才能拿到最新value
}
}
reject(reason) {
if (this.status == PENDING) {
this.reason = reason
this.status = REJECTED
}
}
// 5.then方法
// 判断两个回调是否为函数,不是则忽略(透传)
then(onFulfilled, onRejected) {
const realOnFulfilled = this.isFunction(onFulfilled)
? onFulfilled
: (value) => {
return value
}
const realOnRejected = this.isFunction(onRejected)
? onRejected
: (reason) => {
throw reason
}
const promise2 = new MPromise((resolve, reject) => {
//6. onFulfilled或onRejected执行抛出异常时,promise2需要被reject()
const fulfilledMicrotask = () => {
try {
const x = realOnFulfilled(this.value)
this.resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}
const rejectedMicrotask = () => {
try {
const x = realOnRejected(this.reason)
this.resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}
switch (this.status) {
case FULFILLED: {
fulfilledMicrotask()
break
}
case REJECTED: {
rejectedMicrotask()
break
}
case PENDING: {
this.FULFILLED_CALLBACK_LIST.push(fulfilledMicrotask)
this.REJECTED_CALLBACK_LIST.push(rejectedMicrotask)
break
}
}
})
return promise2
}
isFunction(cb) {
return typeof cb === 'function'
}
resolvePromise(promise2, x, resolve, reject) {
if (promise2 == x) {
return reject(
new TypeError('The promise and the return value are the same')
)
}
if (x instanceof MPromise) {
//如果x是promise,那么让新的promise接收x的状态
// 即继续执行x,如果执行又拿到了y,那么继续解析y
//(return 出来是个promise就拿到外面来then执行相应的回调)
x.then((y) => {
this.resolvePromise(promise2, y, resolve, reject)
}, reject)
} else if (typeof x === 'object' || this.isFunction(x)) {
//x是对象或函数
if (x === null) {
return resolve(x)
}
let then = null
// 如果 x.then 这步出错,那么 reject promise with e as the reason.
try {
then = x.then
} catch (error) {
return reject(error)
}
// 如果获取到的then是个函数
if (this.isFunction(then)) {
let called = false //一个回调只能被调用一次,变量标记是否被调用
try {
then.call(
x,
(y) => {
if (called) {
return
}
called = true
this.resolvePromise(promise2, y, resolve, reject)
}, //onFulfilled
(r) => {
if (called) {
return
}
called = true
reject(r)
} //onRejected
)
} catch (error) {
if (called) {
return
}
reject(error)
}
} else {
resolve(x)
}
} else {
//x为普通值
resolve(x)
}
}
}
// 使用实现
const promise1 = new MPromise((resolve, reject) => {
resolve()
})
const promise2 = promise1.then(
(onFulfilled) => {},
(onRejected) => {}
)