「这是我参与11月更文挑战的第8天,活动详情查看:2021最后一次更文挑战」。
传送门
从零手撕Promise,掌握Promise的实现原理(1)之promise基本结构的实现 从零手撕Promise,掌握Promise的实现原理(2)之基础版本的promise实现 从零手撕Promise,掌握Promise的实现原理(3)之回调地狱是什么 从零手撕Promise,掌握Promise的实现原理(4)之then方法链式调用的初步实现 从零手撕Promise,掌握Promise的实现原理(5)之then方法链式调用的进阶实现 从零手撕Promise,掌握Promise的实现原理(6)之then方法的回调为什么是异步微任务 从零手撕Promise,掌握Promise的实现原理(7)then方法链式调用之核心方法resolvePromise
回顾
到此时我们的Promise
实现已经接近尾声了,现在我们的Promsie长这样
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
const resolvePromise = (promise, x, resolve, reject) => {
// 1.首先判断`x`是基础类型数据,还是引用类型,基础类型的数据直接`resolve`,即可。
if(x !== null && /^(object|function)$/.test(typeof x)){
let then
// 2. 如果是引用类型的数据,尝试获取`x`上的`then`属性(`x.then`),如果在获取属性的时候报异常则`reject`
try{
then = x.then
}catch(e){
reject(e)
}
//3. 判断`then`是否是函数,如果是一个函数则我们认定它为`Promise`,如果不是则`resolve`
if(typeof then === 'function'){
try{
then.call(x, (y) => {
resolvePromise(promise, y, resolve, reject)
},(r) => {
reject(r)
})
}catch(e){
reject(e)
}
}else{
resolve(x)
}
}else{
//基础类型数据直接resolve
resolve(x)
}
}
class Promise{
constructor(executor){
this.state = PENDING
this.value = undefined
this.reason = undefined
//存放onFulfilled
this.onResolvedCallbacks = []
//存放onRejected
this.onRejectedCallbacks = []
const resolve = (value) => {
if (this.state === PENDING) {
this.value = value
this.state = FULFILLED
//promise实例状态改变后调用暂存的onFulfilled
this.onResolvedCallbacks.forEach(fn => fn())
}
}
const reject = (reason) => {
if (this.state === PENDING) {
this.reason = reason
this.state = REJECTED
//promise实例状态改变后调用的onRejected
this.onRejectedCallbacks.forEach(fn => fn())
}
}
try {
//executor函数执行过程中出错,将会导致Promise失败
executor(resolve,reject)
} catch (error) {
reject(error)
}
}
then(onFulfilled, onRejected){
let promise = new Promise((resolve, reject) => {
switch(this.state){
case FULFILLED:
setTimeout(() => {
try{
let x = onFulfilled(this.value)
resolvePromise(promise, x, resolve, reject)
} catch(e){
reject(e)
}
})
break
case REJECTED:
setTimeout(() => {
try{
let x = onRejected(this.reason)
resolvePromise(promise, x, resolve, reject)
} catch(e){
reject(e)
}
})
break
default:
this.onResolvedCallbacks.push(() => {
setTimeout(() => {
try{
let x = onFulfilled(this.value)
resolvePromise(promise, x, resolve, reject)
} catch(e){
reject(e)
}
})
})
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try{
let x = onRejected(this.reason)
resolvePromise(promise, x, resolve, reject)
} catch(e){
reject(e)
}
})
})
}
})
return promise
}
}
关于判断是否是Promise的解释
- 不知道大家有没有疑问,这里判断
Promise
的方式是,一个对象或者函数并且有一个then
属性,这个then
属性也是一个函数,那我们就认为是Promise
。 - 那么为什么不用
instanceof
呢? 这里其实Promise A+
替我们想到了,就是说我们是实现的Promise
和别人实现的Promise
是可以混合共用的,因此我们不能通过instanceof
去单一的判断他是否属于某个类的实例。 - 例如下面这个场景(只是为了说明原因我写的是伪代码)
let Mypromise = require('xxApromise')
let Bpromise = require('xxBpromise')
let p = new Mypromise((resolve,reject) => {
resolve('我们的Promise')
}).then(res => {
return new Bpromise(resolve => {
resolve('别人的Promise')
})
}).then(res => {
consle.log(res)
}, reason => {
})
- 当我们两种
Promise
混用的时候,例如这里,Mypromise
是我们自己实现的,Bpromise
是别人实现的。 - 我们在
then
里返回了别人的Bpromise
,回想我们Promise
的实现过程,这里会由resolvePromise
去处理别人的Bpromise
的实例,那我们通过instanceof
,去判断是不是Promise
,就不是很通用了。 - 因此我们采用一种通用的方法,是对象或者函数,有一个
then
属性是函数,我们就说他是Promise
,当然这种方式有缺点,但是Promise A+
是这么规定的。 - 因为可以混用,所以
Promise A+
里又规定,我们需要加一个变量去限制一下,在如下代码的地方,我用算是比较显眼的注释标识出来了。
const resolvePromise = (promise, x, resolve, reject) => {
// 1.首先判断`x`是基础类型数据,还是引用类型,基础类型的数据直接`resolve`,即可。
if(x !== null && /^(object|function)$/.test(typeof x)){
let then
// 2. 如果是引用类型的数据,尝试获取`x`上的`then`属性(`x.then`),如果在获取属性的时候报异常则`reject`
try{
then = x.then
}catch(e){
reject(e)
}
//3. 判断`then`是否是函数,如果是一个函数则我们认定它为`Promise`,如果不是则`resolve`
if(typeof then === 'function'){
let called = false //**************************************这里加了变量*************************//////
try{
then.call(x, (y) => {
if(called) return //**************************************这里加了变量*************************//////
called = true
resolvePromise(promise, y, resolve, reject)
},(r) => {
if(called) return//**************************************这里加了变量*************************//////
reject(r)
})
}catch(e){
if(called) return//**************************************这里加了变量*************************//////
reject(e)
}
}else{
resolve(x)
}
}else{
//基础类型数据直接resolve
resolve(x)
}
}
- 那么这个变量是用来干啥的呢?
- 其实就是为了在混用的时候(例如下面的代码),防止别人写的
BPromsie
,实现不规范,在调用了resolve, 再去调用reject 还起作用。 - 因此我们这里要判断,在
resolvePromise
中增加的变量就是这个作用,使其调用了then
中的一个回调,另一个就被限制了,不能再次被调用。
let Mypromise = require('xxApromise')
let Bpromise = require('xxBpromise')
let p = new Mypromise((resolve,reject) => {
resolve('我们的Promise')
}).then(res => {
return new Bpromise(resolve => {
resolve('别人的Promise')
})
}).then(res => {
consle.log(res)
}, reason => {
})
resolvePromise
与then
方法中还有一点小瑕疵,处理一下,我们就可以大功告成了
我们现在的Promise
长这样
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
const resolvePromise = (promise, x, resolve, reject) => {
// 1.首先判断`x`是基础类型数据,还是引用类型,基础类型的数据直接`resolve`,即可。
if(x !== null && /^(object|function)$/.test(typeof x)){
let then
// 2. 如果是引用类型的数据,尝试获取`x`上的`then`属性(`x.then`),如果在获取属性的时候报异常则`reject`
try{
then = x.then
}catch(e){
reject(e)
}
//3. 判断`then`是否是函数,如果是一个函数则我们认定它为`Promise`,如果不是则`resolve`
if(typeof then === 'function'){
let called = false //**************************************这里加了变量*************************//////
try{
then.call(x, (y) => {
if(called) return //**************************************这里加了变量*************************//////
called = true
resolvePromise(promise, y, resolve, reject)
},(r) => {
if(called) return//**************************************这里加了变量*************************//////
reject(r)
})
}catch(e){
if(called) return//**************************************这里加了变量*************************//////
reject(e)
}
}else{
resolve(x)
}
}else{
//基础类型数据直接resolve
resolve(x)
}
}
class Promise{
constructor(executor){
this.state = PENDING
this.value = undefined
this.reason = undefined
//存放onFulfilled
this.onResolvedCallbacks = []
//存放onRejected
this.onRejectedCallbacks = []
const resolve = (value) => {
if (this.state === PENDING) {
this.value = value
this.state = FULFILLED
//promise实例状态改变后调用暂存的onFulfilled
this.onResolvedCallbacks.forEach(fn => fn())
}
}
const reject = (reason) => {
if (this.state === PENDING) {
this.reason = reason
this.state = REJECTED
//promise实例状态改变后调用的onRejected
this.onRejectedCallbacks.forEach(fn => fn())
}
}
try {
//executor函数执行过程中出错,将会导致Promise失败
executor(resolve,reject)
} catch (error) {
reject(error)
}
}
then(onFulfilled, onRejected){
let promise = new Promise((resolve, reject) => {
switch(this.state){
case FULFILLED:
setTimeout(() => {
try{
let x = onFulfilled(this.value)
resolvePromise(promise, x, resolve, reject)
} catch(e){
reject(e)
}
})
break
case REJECTED:
setTimeout(() => {
try{
let x = onRejected(this.reason)
resolvePromise(promise, x, resolve, reject)
} catch(e){
reject(e)
}
})
break
default:
this.onResolvedCallbacks.push(() => {
setTimeout(() => {
try{
let x = onFulfilled(this.value)
resolvePromise(promise, x, resolve, reject)
} catch(e){
reject(e)
}
})
})
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try{
let x = onRejected(this.reason)
resolvePromise(promise, x, resolve, reject)
} catch(e){
reject(e)
}
})
})
}
})
return promise
}
}
传送门
从零手撕Promise,掌握Promise的实现原理(1)之promise基本结构的实现 从零手撕Promise,掌握Promise的实现原理(2)之基础版本的promise实现 从零手撕Promise,掌握Promise的实现原理(3)之回调地狱是什么 从零手撕Promise,掌握Promise的实现原理(4)之then方法链式调用的初步实现 从零手撕Promise,掌握Promise的实现原理(5)之then方法链式调用的进阶实现 从零手撕Promise,掌握Promise的实现原理(6)之then方法的回调为什么是异步微任务 从零手撕Promise,掌握Promise的实现原理(7)then方法链式调用之核心方法resolvePromise