promise then err_前端面试题——自己实现promise

如何使用promise是前端程序员必会的知识,但是promise本身实现起来却没有那么容易。本文会手把手带你一点一点实现一个最简单的promise。

MyPromise class基本版

promise这个类最基本的功能就是传入处理器函数executor。根据MDN上的解释:

这个“处理器函数”接受两个函数——resolve 和 reject ——作为其参数。当异步任务顺利完成且返回结果值时,会调用 resolve 函数;而当异步任务失败且返回失败原因(通常是一个错误对象)时,会调用reject 函数。

例如:

const 

这段程序在1秒之后会打印出” promise”,并执行resolve函数,参数为0。

MyPromise的定义如下:

class 

promise中有三个状态:pending,fulfilled以及rejected。在上面这段程序中,使用this.$state来保存promise的状态。

而resolve和reject的函数定义当中,传入的参数res赋给this.$value,从而实现参数res的传递。

最后,调用executor,并把resolve和reject当做两个参数传递进去。如果这中间出现异常,我们会认为这是操作失败,从而调用reject函数。

这是MyPromise这个类调用的时候最基本的功能,虽然有点绕,但是代码还是很直观。接下来我们创建MyPromise的then功能。

MyPromise的then函数基本版

Then这个方法有两个参数:onFulfilled和onRejected。根据MDN:

它们都是 Function 类型。当Promise状态为 fulfilled时,调用 then 的 onfulfilled 方法,当Promise状态为 rejected时,调用 then 的 onrejected 方法。

为了实现then的两个参数会在fulfilled或者rejected的时候被调用,我们需要一个this.$chained来帮忙存储then当中的onFulfilled和onRejected方法,在状态改变之后调用。

代码如下:

class 

我们在上面的promise的基础上执行:

promise

可以看到,程序能在1秒之后正确的打印出来resolved、1st then以及0(就是res的值)。

整个过程是这样的:

首先执行MyPromise的constructor,这其中执行executor函数,定时器开始计时,此时定时器内部的resolve并没有被立即调用。

Constructor执行完毕之后,立即执行promise.then函数。把onFulfilled函数放入this.$chained当中(我并没有定义onRejected函数,这个与onFulfilled函数处理方法类似,不再赘述)。此时,this.$chained这个数组有一个元素:

(

在定时器计时结束的时候,执行定时器中的函数,打印resolved,并执行resolve函数。

在resolve函数内部,依次执行this.$chained内部函数。打印1st then以及0

不过这个简易版本的then有个问题:不能chainable,如果调用

promise

则第二个then调用时候会失败,因为第一个then不返回任何值。chainable是then最重要的功能了。我们继续完善then的功能。

Chainable的then

如何让then可以链式调用呢?MDN上写到:

因为 Promise.prototype.then 和 Promise.prototype.catch 方法返回promise 对象, 所以它们可以被链式调用。

那我们改写一下then函数,让其返回一个promise对象。我们先来看这段代码:

then

我们用这段程序测试一下:

promise

打印结果:

promise

而只有”setTimeout ends”是在第二个1秒之后打印出来的。

我们在分析打印结果为什么不对之前,先看看这一版本的then内部都做了什么:

上面的promise调用chain可以改写成这个样子:

let 

比如,在p0.then当中,把含有callback(onFulfilled(res))的一个函数定义(callback这个函数中的resolve是p1的resolve,而不是p0的resolve)push给了p0的this.$chained中(onRejected情况与上面类似,不再赘述)。当p0被resolve的时候,执行p0的this.$chained中的代码,也就是执行callback(onFulfilled(res))。在这其中,先执行onFulfilled(res),把执行结果传递给callback函数,作为p1中resolve的参数。

而p1中resolve这个函数执行的过程中,又把p1的this.$chained(内部含有p2的resolve信息)依次执行,以此类推。

那么,原因就找到了,在执行p1.then中MyPromise的setTimeout之后,还未等p3被resolve,就会执行p2的this.$chained中的内容,因此p2.then的内容会被立即执行,从而res的赋值也是错误的。

Then处理异步返回值

我们需要在constructor的resolve定义中添加三行代码,添加完毕之后resolve如下所示:

const 

if 

当中,如果onFulfilled(res)的返回结果是一个promise,那么它就一定满足:res != null && typeof res.then === 'function',则p2的resolve函数就会返回res.then(resolve, reject)。这里的res就是p3,而p3.then接收的resolve就是p2的resolve(递归调用)。

在p3被resolve的时候,会跳过if (res != null && typeof res.then === 'function')这个条件,从而执行p2.then当中的内容(p2的chain当中的内容)。

好了,这就是一个简易的promise。希望大家能有所收获!

参考代码:https://thecodebarbarian.com/write-your-own-node-js-promise-library-from-scratch.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值