资讯专栏INFORMATION COLUMN

浅析promise与自定义promise

苏丹 / 710人阅读

重点写在开头吧。
promise 简单用法

new Promise ((res, rej) => {执行函数; res(data)}).then(data => 处理(data))

传统观念上拆分

   1. new Promise -> 实例化过程
   2.(res, rej) => {执行函数; res(data)} -> 执行函数并抛出数据
   3.then(data => process(data)) -> 处理抛出值

但是 实际上 then 主要是用来 声明回调函数,回调函数的执行一般都是在实例化的时候
(因为实例化传入的函数很多都是异步,执行慢;这有点类似于观察者模式,在subscribe中去声明回调,真正的执行实际上在next的时候)
所以:
一般实际执行顺序拆分(按照执行速度拆分排序)

    1.new Promise -> 实例化过程
    2.then(data => process(data)) -> 声明回调函数
    3.(res, rej) => {执行函数; res(data)} -> 用then 中声明的回调函数 处理 这个res抛出的data
   

但是还有一种情况,就是实例化时候的执行函数处理速度超过了then的声明(按照执行速度拆分排序)

   1. new Promise -> 实例化过程
   2.(res, rej) => {执行函数; res(data)} -> 执行函数并抛出数据,并且then尚未声明完成
   3.then(data => process(data)) -> 直接执行函数

简单点说,就是实例化时候,传入的函数,和 then 声明,到底谁先完成这是promise唯一的难点。
这么说,如果“执行函数“ 执行完成,执行res(data)时,then还没声明完,那么回调函数就会为空,这个时候就得让then 中 回调函数在声明完成的之后直接执行
亦或者 ,then 声明完成时候,还没有res(data), 那么就得让then 中声明的方法先加入到一个队列,等待res(data)时候依次执行;

如果这边没有看懂的话,可以继续往下看。有形象化的解释。

promise的用法简单介绍一下,我觉得如果有人愿意看这篇文章,对promise的用法多多少少也了解;

 new Promise((res, rej) => {
        try{
            执行函数;
            res(data)
        } catch(e){
            rej(e);
        }}).then(resCb, rejCb).then(cb);

先说下我个人对promise的理解

1.promise 从表现形式上看,是将执行函数的返回值通过resolve或者reject 抛出data,再在then 函数里面处理data
2.promise 可以链式调用then,上一个then的返回值作为下一个then中函数的实参

但实际上,promise 并非一定是在then 里面执行的,尤其是异步的时候,理应在实例函数中执行,才符合我们对单线程的理解。

promise简单点说分为两部分。一个是实例化 时候,传入的执行函数另一部分为then中传入的回调函数 这个关系就好比 监考老师学生
如果监考老师 先到了教室,那么自然而然,学生是直接依次进入教室;(实例函数执行较快,超过了then的声明)
如果监考老师 在学生们后面到,那么学生们只能按顺序排队在门口等监考老师;(实例函数执行较慢)
也就是说,执行函数和then函数的声明,先后顺序并非固定(一般情况下then声明先完成)
这个时候就需要一个状态码去判断到底是哪种情况("default", "resolve" ,"reject")
为了方便理解,可以把resolve 和 reject看成同一类型
我先直接发代码写注释
时间有限,本次先不考虑then 中函数 存在异步的问题

// 简述下逻辑:
    //1.定义一个state 是用来判断then 和 实例化时候传入的函数 哪一个先完成
    //也就是刚刚说的监考老师和学生的问题。默认值为default, 默认老师没到(实例函数未执行完成)
    //2.定义resolve_ 用来存放then 中定义的方法,相当于学生排队的过道,便于按顺序执行,reject_同理,以下不再重复
    //3.定义resolveData用来记录抛出值,也就是res(data) 中的data,作为then中方法的参数
    //4.在res抛出值的时候,将state改成resolve,相当于表明监考老师到教室了
      //如果resolve_队列中已经有定义函数就依次执行它们,相当于如果有学生就进教室。
    //5.then 方法声明时候,检测状态state 是不是 default ,如果是,说明还没有抛出值,
      //相当于监考老师还没到,学生都去排队,加入到resolve_队列; 
      //如果状态已经不是default ,那么说明监考老师已经到了,学生不用排队,直接进教室;也就是方法直接执行
  
new promise_( (res, rej) => res(3)).then(data=> {console.log(data);return 6}).then(data => console.log(data))// 3,6

针对实际例子说一下
上面的例子明显就是 res(3) 先执行完成,然后执行then 的函数
相当于监考老师先到了教室,那么,then 中的定义的函数就应该直接执行

new promise_( (res, rej) => setTimeout( () => res(3), 1000).then(data=> {console.log(data);return 6}).then(data => console.log(data))// 3,6

上面的例子明显就是 then 先定义完成,然后res才抛出值
相当于监考老师后到了教室,那么,then 中的定义的函数就应该默默排队等待

因为 then 和 实例函数 两者顺序并不确定,所以 then 时候要通过state 判断实例函数 是否执行完成,
同时,实例函数执行完成抛出值时,也需要检测一下resolve_队列,判断then 是否已经声明完成。
下面是我自己写的promise的代码

class promise_ {

    constructor(func) {

        if(typeof func !== "function"){
            throw Error("实例化中传参必须为函数");
            return;
        } // 判断下是否传入的是函数 ,与逻辑无关,直接往下看

        this.state = "default"; //用来判断
        this.resolve_ = []; // then中reslove 的方法队列,用来接收resolve方法
        this.reject_ = []; // then中的reject 的方法队列,用来接收reject方法
        this.resolveData = null; // resolve的抛出值
        this.rejectData = null; // reject的抛出值 

        func(this.resolve.bind(this), this.reject.bind(this)); //实例化时候传入的函数

    }
    
    then(cb, errCb) {

        if(typeof cb !== "function" || (errCb && typeof cb !== "function")) {
            throw Error("then参数,成功回调函数,失败回调函数");
            return;
        } // 日常判断是不是传参正确,和逻辑无关

        switch(this.state) { // 检测一下监考老师来没来教室
            case "default": this.resolve_.push(cb); this.reject_.push(errCb); break;// 如果监考老师没到场,排队
            case "resolve": this.resolveData = cb(this.resolveData); break; // 如果监考老师到场,直接进来,因为链式调用,所以执行后把抛出值改一下,方便后面的then 用
            case "reject": this.rejectData = errCb(this.rejectData); break;// 如果监考老师到场,直接进来,因为链式调用,所以执行后把抛出值改一下,方便后面的then 用
            default: break; 
        }

        return this; // 链式调用
    }

    resolve(data) {

        if(!data) return; // 判断有无参数,和逻辑无关

        this.resolveData = data; // 设定抛出值
        this.state = "resolve"; // 将状态设置为resolve ,表示监考老师到场了
        while(this.resolve_.length)  // 先进先出,依次执行then 中函数
            this.resolvedata = this.resolve_.shift()(data);
    }

    reject(data) {

        if(!data) return;

        this.rejectData = data;
        this.state = "reject";
        while(this.reject_.length) 
            this.rejectData = this.reject_.shift()(data);
    }

}


文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/105091.html

相关文章

  • 浅析 Vue 2.6 中的 nextTick 方法

    摘要:核心的异步延迟函数,用于异步延迟调用函数优先使用原生原本支持更广,但在的中,触摸事件处理程序中触发会产生严重错误的,回调被推入队列但是队列可能不会如期执行。 浅析 Vue 2.6 中的 nextTick 方法。 事件循环 JS 的 事件循环 和 任务队列 其实是理解 nextTick 概念的关键。这个网上其实有很多优质的文章做了详细介绍,我就简单过过了。 以下内容适用于浏览器端 JS,...

    fobnn 评论0 收藏0
  • Vue.nextTick浅析

    摘要:浅析的特点之一就是响应式,但数据更新时,并不会立即更新。尽管已经更新,但新增的元素并不立即插入到中。实际在中,执行了,这也是自动绑定到执行上下文的原因。在内,使用数组保存回调函数,表示当前状态,使用函数来执行回调队列。 Vue.nextTick 浅析 Vue 的特点之一就是响应式,但数据更新时,DOM 并不会立即更新。当我们有一个业务场景,需要在 DOM 更新之后再执行一段代码时,可以...

    MartinDai 评论0 收藏0
  • 浅析es6-promise源码

    摘要:主要逻辑本质上还是回调函数那一套。通过的判断完成异步和同步的区分。 主要逻辑: 本质上还是回调函数那一套。通过_subscribers的判断完成异步和同步的区分。通过 resolve,reject -> publish -> invokeCallback -> resolve,reject的递归和下一条then的parent是上一条的child来完成then链的流转 同步情况...

    fox_soyoung 评论0 收藏0
  • 浅析Promise

    摘要:声明,只是简单地提了提的基本使用,大牛绕道先看一个例子你需要知道是个库返回一个实例你可以把理解为,它们功能相近,只不过遵循规范我是第二个我是第二个为了防止你对这个链式调用看得眼花缭乱,我把这个给简化一下成功回调失败回调成功回调失败回调,准备 声明,只是简单地提了提promise的基本使用,大牛绕道^_^ 1.先看一个例子 你需要知道1.axios是个库2.axios()返回一个Prom...

    darkerXi 评论0 收藏0
  • 浅析 JS 事件循环之 Microtask 和 Macrotask

    摘要:常见应用则是为了完成一些更新应用程序状态的较小的任务,如处理的回调和的修改,以便让这些任务在浏览器重新渲染之前执行。常见应用执行顺序的实现需要至少一个和至少一个。 简介 我们在上一篇 《浅析 JS 中的EventLoop 事件循环》 中提到一个 Event Queue,其实在事件循环中 queue 一共有两种,还有一种叫 Job Queue 其中 Event Queue 在 HTML...

    sihai 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<