手写promise

    promise是一个基于原型实现的类,可以被链式then调用,是业务中解决异步的常用措施

\bullet 前言

    关于promise的基础语法,可以参考我两年前写的不成熟小文以及蹩脚的使用示例,promise相关的常见面试题也可以参考这里

\bullet 规范或定义

    \ast 术语

        promise:类实例,通过new获取,接受一个可选的函数类型参数

        thenable:一个可链式调用的函数,用于获取promise的值

        value:成功的返回值,对应resolve函数

        reson:失败的原因,对应rejected

        exception:异常,对应throw

    \ast 三种状态:pending、fulfilled、rejected

        pending:初始状态,可通过resolve或rejected修改状态

        fulfilled:成功状态,由resolve触发

        rejected:失败状态,由reject触发

    \ast then

         用于获取上一个promise的值,包括成功和失败

          onFulfilled和onRejected仅在状态改变时被执行一次,且这两个函数应该是微任务(queueMicrotask)

          可被多次注册多次执行,当状态改变时需要按注册的先后顺序执行

          支持链式调用

          回调函数传出的结果应当进行解析保持格式统一

          当回调过程中throw error时,统一适用reject拒绝

           若回调不是函数,应当向上取值

    \ast 解析parsePromise

            接受四个参数:promise,x,resolve,reject

            若promise===x,则reject掉

            若x是promise

                    当前是pending状态,等待

                    当前是fulfilled状态,返回该value

                    当前是rejected状态,返回该reson

            若为对象

                    避免报错try...catch

            若为函数

                    修正this指向,并递归解析

            否则

                    返回x

\bullet 实现

    \ast 初始化

        定义三种状态标识:pending、fulfilled、rejected

        定义辅助函数isFunc判断是否传入的是函数,对于传入的onReject或onFulfilled如果不为函数应该直接返回value或reason

        定义成功和失败的返回结果容器value和reason

        定义status存储当前的promise状态

    \ast 定义resole和reject接口,用于修改promise的状态,该状态仅在status为pending时执行

    \ast 接收入参,应当立即调用,并将resolve和reject函数抛出以等待合适的时机修改status

(该入参是同步任务)

    \ast 定义then函数,并作简单的容错处理,若传入的不是函数类型,则包装一个默认函数;为了实现链式调用,在最后返回一个新的MyPromise实例

    \ast  由于new Promise是同步任务,故存在直接修改状态的情况,即在new Promise的回调中直接调用resolve或reject。那么在.then返回的promise中需要返回上一次的值,即this.value或this.reason

(在上一个MyPromise被resolve或reject后调用then,返回的新的MyPromise由于又是同步立即执行,故此时框红位置访问到的是上一次的值)

            示例

    \ast 同样的,如果第一个new Promise回调时,也可能是执行了异步逻辑如宏任务微任务又或是ajax,此时直接调用.then由于未调用resolve或reject仍为pending,无法获取上一次的结果,需要等待状态改变后再计算结果抛出;且.then可被注册多次,每一级的then都需要等待上一次的then返回的promise的结果。故使用数组先将它们存起来等待调用时机

(只有第一个会存在异步,必将库内部不可能主动帮你去生成一个异步代码)

    \ast 由于只有第一个会被阻塞,故只需要监听第一个status由pending变为fulfilled或rejected即可

(使用私有变量_status是为了避免死循环;框黄区域是之前实现的缺陷,因为需要在 this.FULFILLED_CBS 遍历过程中拿到this.value并传递给下一个.then)

            示例

    \ast 除了同步和异步,还可能在回调中写了错误的代码,对于同步代码,其在constructor中会被直接执行,而异步逻辑则在监听到status时遍历执行,故异常处理应当有两处

                                示例

            示例

        \star 注意

                必须显示的调用resolve,否则不会触发status的改变,从而.then无法执行

                在setTimeout中书写的错误无法也不应当在类内部捕获

    \ast 为了不阻塞代码,需要将resolve和reject的执行修改为微任务

                            示例

(不使用queueMicrotask 情况下输出为111-- promise 1 -- 222)

    \ast resolve和reject作为函数,允许存在返回值,返回值将透传到下一个then中。需要对返回值作进一步解析

        若返回值是实例本身,则应当报循环引用错误(a返回的直接是实例本身,也可能a返回的是一个Promise,但是该Promise返回的是a)

        若返回值是MyPromise实例,则应该对其解析获取其resolve或reject的值

        若为对象且非null,则尝试取then,并解析其resolve或reject(对上一个递归求值

        否则直接返回

    \ast 对于catch而言,也是返回一个promise以链接调用。且该方法专门用于获取reason。恰好,then方法也是用来获取value和reason的,也恰好返回是新的promise

    \ast 原生promise也支持不new的方式创建一个promise实例,对应静态方法resolve和reject

    \ast 原生的race的表现是,接收多个promise,只要有一个状态先改变了,那么race的结果就是这个已经改变的。需要注意的是:

        传入的值可能不是一个promise,需要调用静态的resolve方法将其转为promise

        需要使用.then进行求值,当上一个promise(使用resolve包装过的)为异步时第二个会放入回调队列中等待

        race也是一个promise,当存在一个先执行完毕时,直接执行race的resolve求值即可

    \ast 原生的all的表现是,所有的promise均为fulfilled时,返回,否则为rejected

         all的逻辑其实和race有点像,只不过这里是取的rejected的值

        定义数组存储then成功数,全部执行时执行resolve



parsePromise的bug

(这里导致this指向错误)

promise.all的问题

    直接arr.push会导致数据的非一一对应,可以通过arr[i]的形式来存储每一个异步对应的结果,同时另外使用变量count作为结束条件

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,588评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,456评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,146评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,387评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,481评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,510评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,522评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,296评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,745评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,039评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,202评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,901评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,538评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,165评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,415评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,081评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,085评论 2 352

推荐阅读更多精彩内容