Promise 链式调用原理精简示例

 更新时间:2022年12月14日 14:59:24   作者:桃小瑞  
这篇文章主要为大家介绍了Promise 链式调用原理精简示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

前言

在面试的过程中,总有一些面试官会问你,手写一个简易版的Promise得行不,得行的话就写一个出来看看,啪一哈,就把纸和笔给了你。 我们思索半天就写出来了个下面这个。 哦豁,高薪张开了它的翅膀,远离了我们。

class Promise {
    constructor (resolve, reject) {}
    resolve () {}
    reject (){}
    then () {}
    catch () {}
    once () {}
    all () {}
    ...
}

本篇文章将不讲述手写出来一个简易的Promise,感兴趣的朋友可以去看我这篇文章 -> Promise详解-手写Promise,实现一款自己的简易Promise

本篇文章记录的是如何实现Promise的核心功能之一的.then 链式调用,采用构造函数的写法,本篇文章的代码不考虑任何容错和异常处理,只单独说明其链式调用原理,方便理解。

先摆上完整代码,去掉注释和一些换行共20行有余。

代码

function CustomPromise (fn) {
    // 回调收集
    this.callbackList = []
    // 传递给Promise处理函数的resolve
    const resolve = (value) => {
        // 注意promise的then函数需要异步执行
        setTimeout(() => {
            // 这里直接往实例上挂个data
            this.data = value;
            // 把callbackList数组里的函数依次执行一遍
            this.callbackList.forEach(cb => cb(value))
        });
    }
    /*
        执行用户传入的函数 
        并且把resolve方法交给用户执行
    */ 
    fn(resolve)
}
/*
    重点
*/
// 往构造函数的原型上挂载.then方法
CustomPromise.prototype.then = function (onReaolved) {
    // return 一个promise 实例
    return new CustomPromise((resolve) => {
        // 往回调数组中插入回调
        this.callbackList.push(()=>{
            const response = onReaolved(this.data)
            // 判断是否是一个 CustomPromise
            if(response instanceof CustomPromise){
                // resolve 的权力被交给了user promise
                response.then(resolve)
            }else{
                // 如果是普通值,直接resolve
                // 依次执行callbackList里的函数 并且把值传递给callbackList
                resolve(response)
            }
        })
    })
}

经典案例

    new CustomPromise((resolve) => {
        setTimeout(() => {
            // resolve1
            resolve(1);
        }, 300);
    }).then((res) => {// then1
        console.log(res);
        // 返回一个 CustomPromise 
        return new CustomPromise((resolve) => {
            setTimeout(() => {
                // resolve2
                resolve(2);
            }, 300);
        });
    }).then(res => {// then2
        console.log(res);
    });

完整的代码和例子已奉上,现在来进行解释。 固然结果很重要,但过程也很重要。我们要做到 知其然知其所以然。

解析

第一步

首先,我们我们先创建这样一个Promise, 这里需要使用匿名函数,不能使用箭头函数,或者你可以根据这个方法已class 类的方法进行实现。

大概步骤如下:

  • 声明构造函数/类
  • 在内部声明一个数组名为callbackList用来装回调,并放到this里面
  • 声明一个名resolve的方法,用来传递给Promise进行处理,注意:resolve 内部需要为异步,这里可以采用 setTimeout 实现
  • 循环callbackList并执行里面的方法

写出来后的样子长这样:

function CustomPromise (fn) {
    // 回调收集
    this.callbackList = []
    // 传递给Promise处理函数的resolve
    const resolve = (value) => {
        // 注意promise的then函数需要异步执行
        setTimeout(() => {
            // 这里直接往实例上挂个data
            this.data = value;
            // 把callbackList数组里的函数依次执行一遍
            this.callbackList.forEach(cb => cb(value))
        });
    }
    /*
        - fn 为用户传进来的函数
        - 执行用户传入的函数 
        - 并且把resolve方法交给用户执行
    */ 
    fn(resolve)
}

第二步

注意:第二步是本篇文章的重点,也是这个核心功能的一个重点。

我们需要往CustomPromise的原型上挂载一个.then的方法。并返回的是一个Promise实例,这里依旧使用的是匿名函数。

完整代码长这样:

// 往构造函数的原型上挂载.then方法
CustomPromise.prototype.then = function (onReaolved) {
    // return 一个promise 实例
    return new CustomPromise((resolve) => {
        // 往回调数组中插入回调
        this.callbackList.push(()=>{
            const response = onReaolved(this.data)
            // 判断是否是一个 CustomPromise
            if(response instanceof CustomPromise){
                // resolve 的权力被交给了user promise
                response.then(resolve)
            }else{
                // 如果是普通值,直接resolve
                // 依次执行callbackList里的函数 并且把值传递给callbackList
                resolve(response)
            }
        })
    })
}

写出来过后,在结合上面的那个例子使用,不能说和原生Promise一模一样,但使用起来的链式效果却是一毛一样。

分析说明,此过程需结合上文中的案例一起阅读

    const promise1 = new CustomPromise((resolve) => {
        setTimeout(() => resolve(1));
    })
    promise1.then((res) => {
        const userPromise = new CustomPromise((resolve) => {
            setTimeout(() => resolve(2), 300);
        });
        return userPromise
    });

说明:

  • 我们把new Promise返回的实例叫做promise1
  • Promise.prototype.then的实现中,我们构造了一个新的promise 返回,叫它promise2 在调用then方法的时候,用户手动构造了一个promise并且返回,用来做异步的操作,叫它userPromise,那么在then的实现中,内部的this其实就指向promise1promise2的传入的fn函数执行了一个this.cbs.push()的操作,其实是往promise1callbackList数组中push了一个函数,等待后续执行
CustomPromise.prototype.then = function (onReaolved) {
    // promise 2
    return new CustomPromise((resolve) => {
        // 往回调数组中插入回调
        this.callbackList.push(()=>{})
    })
}

如果用户传入给thenonResolved方法返回的是个userPromise,那么这个userPromise里用户会自己去在合适的时机 resolvePromise2,那么进而这里的response.then(resolve) 中的resolve就会被执行

if(response instanceof CustomPromise){
    response.then(resolve)
}

再结合上面的经典案例看,我这里再放一遍

    new CustomPromise((resolve) => {
        setTimeout(() => {
            // resolve1
            resolve(1);
        }, 300);
    }).then((res) => {// then1
        console.log(res);
        // userPromise
        return new CustomPromise((resolve) => {
            setTimeout(() => {
                // resolve2
                resolve(2);
            }, 300);
        });
    }).then(res => {// then2
        console.log(res);
    });

then1这一整块其实返回的是promise2,那么then2 其实本质上是promise2.then(()=>{}), 也就是说then2注册的回调函数,其实进入了promise2callbackList回调数组里。 又因为我们刚刚知道,resolve2调用了之后,userPromise 会被resolve,进而触发promise2resolve,进而 promise2里的callbackList数组被依次触发。 这样就实现了用户自己写的resolve2执行完毕后,then2里的逻辑才会继续执行,也就是异步链式调用。

说句题外话,这个有点绕,当时还是看了好一会才看懂。

好了,当你看到这里的时候,这篇文章已经接近尾声了,是时候进行总结了。

总结

本篇文章只是根据其原理实现的一个简易链式调用的过程,真正的Promise并没有这么简单,和上文中的比起来复杂很多,而且涉及到很多的异常、容错、边界等情况的处理。

最后推荐一下Promise A+规范 -> 点我查看规范,很值得去看,相信看完后会对Promise有一个更深的了解。

以上就是Promise 链式调用原理精简示例的详细内容,更多关于Promise 链式调用的资料请关注脚本之家其它相关文章!

相关文章

  • 新手如何快速理解js异步编程

    新手如何快速理解js异步编程

    这篇文章主要介绍了新手如何快速理解js异步编程,异步编程从早期的 callback、事件发布\订阅模式到 ES6 的 Promise、Generator 在到 ES2017 中 async,看似风格迥异,但是还是有一条暗线将它们串联在一起的,,需要的朋友可以参考下
    2019-06-06
  • JS基于设计模式中的单例模式(Singleton)实现封装对数据增删改查功能

    JS基于设计模式中的单例模式(Singleton)实现封装对数据增删改查功能

    这篇文章主要介绍了JS基于设计模式中的单例模式(Singleton)实现封装对数据增删改查功能.结合实例形式分析了javascript基于单例模式结合ajax针对数据库进行增删改查的相关操作技巧,需要的朋友可以参考下
    2018-02-02
  • 用js传递value默认值的示例代码

    用js传递value默认值的示例代码

    这篇文章主要介绍了用js传递value默认值的简单实现,很简单但比较实用,需要的朋友可以参考下
    2014-09-09
  • JavaScript实现随机点名网页

    JavaScript实现随机点名网页

    这篇文章主要为大家详细介绍了JavaScript实现随机点名网页,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-09-09
  • 关于JavaScript中parseInt()的一个怪异行为解决

    关于JavaScript中parseInt()的一个怪异行为解决

    parseInt()是内置的 JS 函数,用于解析数字字符串中的整数,下面这篇文章主要给大家介绍了关于JavaScript中parseInt()的一个怪异行为解决,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-12-12
  • uniapp通过概率实现随机抽奖的项目实践

    uniapp通过概率实现随机抽奖的项目实践

    在很多电商平台或者活动中,都会有类似抽奖赢优惠券的功能,本文主要介绍了uniapp通过概率实现随机抽奖的项目实践,具有一定的参考价值,感兴趣的可以了解一下
    2025-04-04
  • 详解微信小程序-获取用户session_key,openid,unionid - 后端为nodejs

    详解微信小程序-获取用户session_key,openid,unionid - 后端为nodejs

    这篇文章主要介绍了微信小程序获取session_key,openid,unionid的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-04-04
  • JS实现的判断方法、变量是否存在功能示例

    JS实现的判断方法、变量是否存在功能示例

    这篇文章主要介绍了JS实现的判断方法、变量是否存在功能,涉及javascript针对变量、方法判断与异常处理操作技巧,需要的朋友可以参考下
    2018-05-05
  • 使用webpack搭建pixi.js开发环境

    使用webpack搭建pixi.js开发环境

    这篇文章主要介绍了使用webpack搭建pixi.js开发环境,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-02-02
  • JavaScript计算两个日期时间段内日期的方法

    JavaScript计算两个日期时间段内日期的方法

    这篇文章主要介绍了JavaScript计算两个日期时间段内日期的方法,涉及javascript操作时间的技巧,需要的朋友可以参考下
    2015-03-03

最新评论