您现在的位置是:网站首页> 编程资料编程资料

JavaScript自定义Promise实现流程_javascript技巧_

2023-05-24 400人已围观

简介 JavaScript自定义Promise实现流程_javascript技巧_

1. 初始结构

我们先来回顾下 js 里 promise 实例是如何创建的:

let promsie = new Promise((resolve, reject) => { resolve('完成了') })

那这样我们就知道了,在我们创建的 promise 类中的构造函数要传入的是一个函数,而且我们在传入参数的时候,这个函数参数会被自动执行,接下来这个函数参数也有自己的参数,也就是 resolve 和 reject 这两个参数,那我们也得让我们手写的 myPromise 支持这两个参数:

class myPromise { constructor(func) { func(resolve,reject) } }

这么写显然是有问题的,因为我们不知道在哪里调用 resolve 和 reject 这两个参数,因为我们还没有定义这两个对象,这两个对象还是函数,因此下面就要用类方法的形式创建这两个函数:

class myPromise { constructor(func) { func(resolve,reject) } resolve() {} reject() {} }

这样是对的了么?

错啦,因为我们得用 this 才能调用自身的方法,所以要在两个参数前加上 this:

class myPromise { constructor(func) { func(this.resolve,this.reject) } resolve() {} reject() {} }

2. 定义状态

promise 里有三种状态,分别是 pending,fulfilled 和 rejected,要注意状态一经改变就不能再改变了且只能由 pending 转化为 fulfilled 或者 pending 转化为 rejected。所以我们可以提前把这些状态定义好,可以用 const 来创建外部的固定变量,然后将 pending 设置为默认状态,这样在每一个实例被创建以后就会有自身的状态属性可以进行判断和变动了:

class myPromise { static PENDING = '待定' static FULFILLED = '成功' static REJECTED = '拒绝' constructor(func) { this.status = myPromise.PENDING func(this.resolve,this.reject) } resolve() { if (this.status === myPromise.PENDING) { this.status = myPromise.FULFILLED } } reject() { if (this.status === myPromise.PENDING) { this.status = myPromise.REJECTED } } }

当执行 reolve 的时候,如果现在的状态是待定,就让他变为成功, reject 同理。

并且,在执行 resolve 或者 reject 的时候都是可以传入一个参数的,这样后面就能使用这个参数了:

class myPromise { static PENDING = '待定' static FULFILLED = '成功' static REJECTED = '拒绝' constructor(func) { this.status = myPromise.PENDING this.result = null func(this.resolve,this.reject) } resolve(result) { if (this.status === myPromise.PENDING) { this.status = myPromise.FULFILLED this.result = result } } reject(result) { if (this.status === myPromise.PENDING) { this.status = myPromise.REJECTED this.result = result } } }

那现在我们 new 一个 myPromise 的实例,大家觉得会不会成功呢?

3. this指向

承接上文,我们 new 了一个实例对象:

let promise = new myPromise((resolve,reject) => { resolve('小杰学前端') })

下面是输出结果:

控制台不出意外的报错了,其实问题就出在这里:

我们在 resolve 中拿不到 status ,也就是 this 已经跟丢了,这是为什么呢?

因为我们在 new 一个新实例的时候,执行的是 constructor 里的内容,也就是 constructoe 里的 this 指向的是实例对象,但是现在我们是在实例对象被创建后再在外部环境下执行 resolve 方法的,这里的 resolve 看着像是和实例一起执行的,其实不然。解决 class 的 this 指向问题一般会用箭头函数, bind 或者 proxy,这里我们就用 bind 来绑定

class myPromise { static PENDING = '待定' static FULFILLED = '成功' static REJECTED = '拒绝' constructor(func) { this.status = myPromise.PENDING this.result = null func(this.resolve.bind(this),this.reject.bind(this)) } resolve(result) { if (this.status === myPromise.PENDING) { this.status = myPromise.FULFILLED this.result = result } } reject(result) { if (this.status === myPromise.PENDING) { this.status = myPromise.REJECTED this.result = result } } }

这里就是给实例的 resolve 和 reject 方法绑定这个 this 为当前的实例对象

或者我们也可以这样解决:

class myPromise { static PENDING = '待定' static FULFILLED = '成功' static REJECTED = '拒绝' constructor(func) { this.status = myPromise.PENDING this.result = null let resolve = (result) => { if (this.status === myPromise.PENDING) { this.status = myPromise.FULFILLED this.result = result } } let reject = (result) => { if (this.status === myPromise.PENDING) { this.status = myPromise.REJECTED this.result = result } } func(resolve, reject) } }

4. then 方法

我们先回顾一下原生的 then 是怎么写的:

let promise = new Promise((resolve, reject) => { resolve('完成了') reject('失败了') }) promise.then( res => console.log(res), result => console.log(result))

那我们就开始写我们的 then ,首先知道的是要传递两个参数,他们都是函数类型,一个表示状态成功时的回调,一个表示状态失败时的回调:

class myPromise { static PENDING = '待定' static FULFILLED = '成功' static REJECTED = '拒绝' constructor(func) { this.status = myPromise.PENDING this.result = null func(this.resolve.bind(this),this.reject.bind(this)) } resolve(result) { if (this.status === myPromise.PENDING) { this.status = myPromise.FULFILLED this.result = result } } reject(result) { if (this.status === myPromise.PENDING) { this.status = myPromise.REJECTED this.result = result } } then(onFulfilled, onRejected) { if (this.status === myPromise.FULFILLED) { onFulfilled(this.result) } if (this.status === myPromise.REJECTED) { onRejected(this.result) } } }

这里 then 写的也是浅显易懂,我们测试一下能否正常输出:

let promise = new myPromise((resolve,reject) => { resolve('成功了') reject('失败了') } promise.then(res => console.log(res), result => console.log(result)) // 成功了

5. 执行异常

如果我们在 new Promise 的时候,执行函数里面我们抛出错误,是会触发拒绝方法,可以把错误的信息作为内容输出出来:

let promise = new Promise((resolve, reject) => { throw new Error('抛出错误') }) promise.then( res => console.log(res), result => console.log(result)) //Error: 抛出错误

但是如果我们在 myPromise 这里写上同样的代码则会报错,于是我们就可以在执行 resolve 和 reject 之前进行判断,可以用 try 和 catch 来该写代码,如果没有报错就正常执行,有报错就调用 reject 方法:

constructor(func) { this.status = myPromise.PENDING this.result = null try { func(this.resolve.bind(this),this.reject.bind(this)) } catch (error) { this.reject(error) } }

注意,在 catch 这里不需要用 bind ,因为这是立即调用的,只在内部执行的。

那到了这里我们的异常处理是不是和原生的差不多了呢,其实还没有,我们知道在 then 里传递的是两个函数参数,那如果我们故意在原生代码里不传入函数作为参数呢:

let promise = new Promise((resolve, reject) => { resolve('成功了') }) promise.then( undefined, result => console.log(result)) 

实际上,这里什么也没有输出,但是如果我们以同样的方法应用在 myPromise 里会发生什么呢?

答案是会报一堆错误,这不是我们想要的,那我们只需要在执行 then 里的参数前进行类型判断,如果它是函数就把原函数参数赋给它,否则就赋给它空函数:

then(onFulfilled, onRejected) { onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : () => {} onRejected = typeof onRejected === 'function' ? onRejected : () => {} if (this.status === myPromise.FULFILLED) { onFulfilled(this.result) } if (this.status === myPromise.REJECTED) { onRejected(this.result) } }

现在再运行就不会报错了。

6. 支持异步(重头戏1)

现在我们的手写代码里面还没有植入异步功能,首先我们先了解一下原生 Promise 的一些运行顺序规则:

console.log(111) let promise = new Promise((resolve, reject) => { console.log(222) resolve('成功了') }) console.log(333) promise.then( res => console.log(res), result => console.log(result)) 

熟悉 JS 执行机制和 prmoise 的同学一定能够正确的判断输出的顺序:

我们再看一下手写的 myPromise 是如何输出的:

console.log(111) let promise = new myPromise((resolve,reject) => { console.log(222) resolve('成功了') }) promise.then(res => console.log(res), result => console.log(result)) // 成功了 console.log(333)

执行代码,查看输出:

那我们已经找到问题的所在了,那就是 resolve 和 reject 里面都没有设置异步执行,二话不说直接套上定时器:

then(onFulfilled, onRejected) { onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : () => {} onRejected = typeof onRejected === 'function' ? onRejected : () => {} if (this.status === myPromise.FULFILLED) { setTimeout(() => { onFulfilled(this.result) }, 0); } if (this.status === myPromise.REJECTED) { setTimeout(() => { onRejected(this.result) }, 0); } }

现在我们的 then 里面就是异步执行的了,再次运行代码查看输出:

可以看到我们成功输出了,但是现在异步的问题真的解决了吗,现在又进入另一个非常!非常!重要的重点了,我们先看一下原生 Promise 这段代码:

console.log(111) let promise = new Promise((resolve, reject) => { console.log(222) setTimeout(() => { resolve('成功了') console.log(444) }, 0); }) console.log(333) promise.then( res => console.log(res), result => console.log(result))

这段代码的输出如下:

在 setTimeout 中会先执行输出444,再执行 resolve ,我们再看一下同样的代码放在 myPromise 中的输出结果:

并没有输出 “ 成功了 ”,这是为什么呢?

没有输出很可能的原因是 then 方法没有被执行,我们再各个位置输出一下当前的状态,进行判断:

console.log(111) let promise = new myPromise((resolve,reject) => { console.log(222) setT
                
                

-六神源码网