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 链式调用的资料请关注我们其它相关文章!

(0)

相关推荐

  • JavsScript中Promise的错误捕获详解

    目录 我们需要在异步任务中准确的进行错误捕获,以便我们可以知道错误出在什么地方 我们再讨论then方法中的第二个参数和Promise.catch方法的区别 题: then方法的连续调用,怎么能够知道是第几个then方法报错了呢. 总结 我们需要在异步任务中准确的进行错误捕获,以便我们可以知道错误出在什么地方 如果对Promise和trycatch不够理解的话,很多时候会出现Promise中的错误无法被捕获的情况,本文来讨论这些情况 try catch try catch 只能捕获当前上下文中的错

  • 捕获未处理的Promise错误方法

    为了保证可读性,本文采用意译而非直译,并且对源代码进行了大量修改.另外,本文版权归原作者所有,翻译仅用于学习. 使用Promise编写异步代码时,使用reject来处理错误.有时,开发者通常会忽略这一点,导致一些错误没有得到处理.例如: function main() { asyncFunc() .then(···) .then(() => console.log('Done!')); } 由于没有使用catch方法捕获错误,当asyncFunc()函数reject时,抛出的错误则没有被处理.

  • Promise抛出错误解决基础示例详解

    目录 then catch finally Promise.resolve Promise.reject then then 函数的会接收两个回调函数,一个是 onFulfilled 函数,一个是 onRejected 函数 如果这两个回调函数没有写返回值,默认会 return undefined; 进入下一个函数的 onFulfilled 函数中 const p = new Promise((resolve, reject) => { resolve(22); }); p.then( (suc

  • 详解JavaScript如何实现一个简易的Promise对象

    目录 前言 Promise的基础结构与用法 使用class类实现promise对象 写在最后 前言 实现一个简易的Promise对象,我们首先要了解几个相关的知识点: Promise对象的状态: pending(进行中).fulfilled(已成功)和rejected(已失败).只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态.这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变. Promise的参数: Promise构造函数接收一个函

  • JS使用Promise时常见的5个错误总结

    目录 1.避免 Promise 地狱 2.在 Promise 中使用 try/catch 块 3.在 Promise 块内使用异步函数 4.在创建 Promise 后立即执行 Promise 块 5.不一定使用 Promise.all() 方法 总结 Promise 提供了一种优雅的方法来处理 JS 中的异步操作.这也是避免“回调地狱”的解决方案.然而,并没有多少开发人员了解其中的内容.因此,许多人在实践中往往会犯错误. 在本文中,介绍一下使用 promise 时的五个常见错误,希望大家能够避免

  • Promise对象all与race方法手写示例

    目录 前言 Promise.all 介绍 手写 Promise.race 介绍 手写 前言 在理解了手写promsie.then的方法后,再来看它的其他方法,感觉真的简单了不少. Promise.all 介绍 Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例. const p = Promise.all([p1, p2, p3]); 上面代码中,Promise.all()方法接受一个数组作为参数,p1.p2.p3都是 Promise 实例.另外

  • JavaScript模拟实现Promise功能的示例代码

    模拟Promise的功能,  按照下面的步骤,一步一步 1. 新建是个构造函数 2. 传入一个可执行函数 函数的入参第一个为 fullFill函数 第二个为 reject函数: 函数立即执行, 参数函数异步执行 3. 状态一旦更改就不可以变更 只能 pending => fulfilled 或者 pending => rejected 4. then 的时候要处理入参的情况 successCallback 和failCallback 均可能为非函数 默认的 failCallback 一定要将异

  • JavaScript面试必考之实现手写Promise

    目录 Promise手写 框架 完整代码 测试 resolve reject Promise手写 Promise作为面试必考题,Promise的手写也是面试官必问的问题,所以对于Promise我们一定要了解透彻 框架 (function(window) { MyPromise.prototype.then = function (onResolved, onRejected) {} MyPromise.prototype.catch = function (onRejected) {} func

  • Promise 链式调用原理精简示例

    目录 前言 代码 经典案例 解析 第一步 第二步 分析说明,此过程需结合上文中的案例一起阅读 总结 前言 在面试的过程中,总有一些面试官会问你,手写一个简易版的Promise得行不,得行的话就写一个出来看看,啪一哈,就把纸和笔给了你. 我们思索半天就写出来了个下面这个. 哦豁,高薪张开了它的翅膀,远离了我们. class Promise { constructor (resolve, reject) {} resolve () {} reject (){} then () {} catch ()

  • JavaScript链式调用原理与实现方法详解

    本文实例讲述了JavaScript链式调用原理与实现方法.分享给大家供大家参考,具体如下: 1.什么是链式调用? 这个很容易理解,例如 $('text').setStyle('color', 'red').show(); 一般的函数调用和链式调用的区别:链式调用完方法后,return this返回当前调用方法的对象. 首先,我们先来看看一般函数的调用方式 (1)先创建一个简单的类 //创建一个bird类 function Bird(name) { this.name=name; this.run

  • Java及Android中常用链式调用写法简单示例

    本文实例讲述了Java及Android中常用链式调用写法.分享给大家供大家参考,具体如下: 最近发现,目前大火的许多开源框架中,大多都使用了一种"(方法).(方法).(方法)"的形式进行调用,最典型的就是RxJava.android中AlertDialog控件的源码也是这种形式的.查阅可知,大家把它叫做链式调用."行动是检验程序的唯一标准"0.0!查了.说了那么多,还是得自己写个例子并运行出预期的效果. /** * * 链式调用 * * @author k.k *

  • 使用Promise链式调用解决多个异步回调的问题

    介绍 所谓Promise,简单来说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果. 缺少场景支撑,对于新手而言,很难理解Promise的意义. 在<你不知道的JavaScript中>有个场景介绍得很形象: 我走到快餐店的柜台,点了一个芝士汉堡.我交给收银员1.47美元.通过下订单并付款,我已经发出了一个对某个值(就是那个汉堡)的请求.我已经启 动了一次交易. 但是,通常我不能马上就得到这个汉堡.收银员会交给我某个东西来代替汉堡:一张带有 订单号的收据.订单号就是一个

  • axios 拦截器管理类链式调用手写实现及原理剖析

    目录 axios库的拦截器使用 整体设计 拦截器管理类实现 接口定义 代码实现 链式调用实现 axios库的拦截器使用 我们知道axios库的拦截器的使用方式如下: // 添加一个请求拦截器 axios.interceptors.request.use(function (config) { // 在发送请求之前可以做一些事情 return config; }, function (error) { // 处理请求错误 return Promise.reject(error); }); // 添

  • Python实现类似jQuery使用中的链式调用的示例

    关于jQuery的链式调用 真正有意义的链式调用也就是方法链(method chaining).方法链这个词是有的,而且使用的很广泛.其实很多人口中的"链式调用"实际上就是指方法链.但是"链式调用"这个词语还可以描述函数调用链,所以让它自身的存在价值变得难以理解. 我总结的方法链的价值有这么几个: 1. 让调用过程更接近自然语言. 2. 把原本参数列表复杂的方法化作多个参数列表简单的方法来使用. 3. 减少不必要的代码量. 这个三点都是有益于开发的,所以方法链的存在

  • JQuery特殊效果和链式调用操作示例

    本文实例讲述了JQuery特殊效果和链式调用操作.分享给大家供大家参考,具体如下: JQuery的特殊效果 fadeOut()淡入 fadeToggle()切换淡入淡出 hide() 隐藏元素 show() 现实元素 toggle() 切换元素的可见状态 slideDown() 向下展开 slideUp() 向上卷起 slideToggle()依次展开或者卷起某个元素 <!DOCTYPE html> <html lang="en"> <head> &

  • JavaScript DSL 流畅接口(使用链式调用)实例

    认真研究了一会DSL,发现了这么几件有趣的事,JavaScript用得最多的一个东西怕是链式调用 (方法链,即Method Chaining). 有意思的是Martin Flower指出: 复制代码 代码如下: I've also noticed a common misconception - many people seem to equate fluent interfaces with Method Chaining. Certainly chaining is a common tec

  • JavaScript中两种链式调用实现代码

    一.方法体内返回对象实例自身(this) 复制代码 代码如下: function ClassA(){ this.prop1 = null; this.prop2 = null; this.prop3 = null; } ClassA.prototype = { method1 : function(p1){ this.prop1 = p1; return this; }, method2 : function(p2){ this.prop2 = p2; return this; }, metho

  • laravel技巧之查询构造器Query Builder叠加链式调用的方法

    查询构造器简介 Laravel查询构造器(query builder)提供方便.流畅的接口,用来建立及执行数据库查找语法 使用PDO参数绑定,以保护应用程序免于SQL注入.因此传入的参数不需额外转义特殊字符 基本可以满足所有的数据库操作,而且在所有支持的数据库系统上都可以执行 引言 今天给大家介绍一下laravel查询构造器的一个小技巧,在官方文档示例中没有详细提到,也不是啥高端技巧,可能很多人在用了,不知道的同学可以看看. 在业务代码中经常会根据不同条件来查询,举个简单例子,我们现在要查询用户

随机推荐