广告
返回顶部
首页 > 资讯 > 前端开发 > node.js >分析Promise链式调用
  • 225
分享到

分析Promise链式调用

2024-04-02 19:04:59 225人浏览 薄情痞子
摘要

本篇内容介绍了“分析Promise链式调用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!一、前言上一节中,

本篇内容介绍了“分析Promise链式调用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

一、前言

上一节中,实现了 Promise 的基础版本:

//极简的实现+链式调用+延迟机制+状态
class Promise {
    callbacks = [];
    state = 'pending';//增加状态
    value = null;//保存结果
    constructor(fn) {
        fn(this._resolve.bind(this));
    }
    then(onFulfilled) {
        if (this.state === 'pending') {//在resolve之前,跟之前逻辑一样,添加到callbacks中
            this.callbacks.push(onFulfilled);
        } else {//在resolve之后,直接执行回调,返回结果了
            onFulfilled(this.value);
        }
        return this;
    }
    _resolve(value) {
        this.state = 'fulfilled';//改变状态
        this.value = value;//保存结果
        this.callbacks.forEach(fn => fn(value));
    }
}

但链式调用,只是在 then 方法中 return 了 this,使得 Promise 实例可以多次调用 then 方法,但因为是同一个实例,调用再多次 then 也只能返回相同的一个结果,通常我们希望的链式调用是这样的:

//使用Promise
function getUserId(url) {
    return new Promise(function (resolve) {
        //异步请求
        Http.get(url, function (id) {
            resolve(id)
        })
    })
}
getUserId('some_url').then(function (id) {
    //do something
    return getNameById(id);
}).then(function (name) {
    //do something
    return getCourseByName(name);
}).then(function (course) {
    //do something
    return getCourseDetailByCourse(course);
}).then(function (courseDetail) {
    //do something
});

每个 then 注册的 onFulfilled 都返回了不同的结果,层层递进,很明显在 then 方法中 return this 不能达到这个效果。引入真正的链式调用,then 返回的一定是一个新的Promise实例。

分析Promise链式调用

真正的链式 Promise 是指在当前 Promise 达到 fulfilled 状态后,即开始进行下一个 Promise(后邻 Promise)。那么我们如何衔接当前 Promise 和后邻 Promise 呢?(这是理解 Promise 的难点,我们会通过动画演示这个过程)。

二、链式调用的实现

先看下实现源码

//完整的实现
class Promise {
    callbacks = [];
    state = 'pending';//增加状态
    value = null;//保存结果
    constructor(fn) {
        fn(this._resolve.bind(this));
    }
    then(onFulfilled) {
        return new Promise(resolve => {
            this._handle({
                onFulfilled: onFulfilled || null,
                resolve: resolve
            });
        });
    }
    _handle(callback) {
        if (this.state === 'pending') {
            this.callbacks.push(callback);
            return;
        }
        //如果then中没有传递任何东西
        if (!callback.onFulfilled) {
            callback.resolve(this.value);
            return;
        }
        var ret = callback.onFulfilled(this.value);
        callback.resolve(ret);
    }
    _resolve(value) {
        this.state = 'fulfilled';//改变状态
        this.value = value;//保存结果
        this.callbacks.forEach(callback => this._handle(callback));
    }
}

由上面的实现,我们可以看到:

  • then 方法中,创建并返回了新的 Promise 实例,这是串行Promise的基础,是实现真正链式调用的根本。

  • then 方法传入的形参 onFulfilled 以及创建新 Promise 实例时传入的 resolve 放在一起,被push到当前 Promise 的 callbacks 队列中,这是衔接当前 Promise 和后邻 Promise 的关键所在。

  • 根据规范,onFulfilled 是可以为空的,为空时不调用 onFulfilled。

看下动画演示:

分析Promise链式调用

(Promise 链式调用演示动画)

当第一个 Promise 成功时,resolve 方法将其状态置为 fulfilled ,并保存 resolve 带过来的value。然后取出 callbacks 中的对象,执行当前 Promise的 onFulfilled,返回值通过调用第二个 Promise 的 resolve 方法,传递给第二个 Promise。动画演示如下:

分析Promise链式调用

(Promise 链式调用 fulfilled)

为了真实的看到链式调用的过程,我写一个mockajax函数,用来模拟异步请求:


const mockAjax = (url, s, callback) => {
    setTimeout(() => {
        callback(url + '异步请求耗时' + s + '秒');
    }, 1000 * s)
}

除此之外,我给 Promise 的源码加上了日志输出并增加了构造顺序标识,可以清楚的看到构造以及执行过程:

//Demo1
new Promise(resolve => {
  mockAjax('getUserId', 1, function (result) {
    resolve(result);
  })
}).then(result => {
  console.log(result);
})

【 Demo1 的源码】

执行结果如下:

[Promse-1]:constructor
[Promse-1]:then
[Promse-2]:constructor
[Promse-1]:_handle state= pending
[Promse-1]:_handle callbacks= [ { onFulfilled: [Function], resolve: [Function] } ]
=> Promise { callbacks: [], name: 'Promse-2', state: 'pending', value: null }
[Promse-1]:_resolve
[Promse-1]:_resolve value= getUserId异步请求耗时1秒
[Promse-1]:_handle state= fulfilled
getUserId异步请求耗时1秒
[Promse-2]:_resolve
[Promse-2]:_resolve value= undefined

通过打印出来的日志,可以看到:

  1. 构造 Promise-1 实例,立即执行 MackAjax('getUserId',callback);

  2. 调用 Promise-1 的 then 方法,注册 Promise-1 的 onFulfilled 函数。

  3. then 函数内部构造了一个新的 Promise实例:Promise-2。立即执行 Promise-1 的 _handle方法。

  4. 此时 Promise-1 还是pending的状态。

  5. Promise-1._handle 中就把注册在 Promise-1 的 onFulfilled 和 Promise-2 的 resolve 保存在 Promise-1 内部的 callbacks。

  6. 至此当前线程执行结束。返回的是 Promise-2 的 Promise实例。

  7. 1s后,异步请求返回,要改变 Promise-1 的状态和结果,执行 resolve(result)。

  8. Promise-1 的值被改变,内容为异步请求返回的结果:"getUserId异步请求耗时1s"。

  9. Promise-1 的状态变成 fulfilled。

  10. Promise-1 的 onFulfilled 被执行,打印出了"getUserId异步请求耗时1秒"。

  11. 然后再调用 Promise-2.resolve。

  12. 改变 Promise-2 的值和状态,因为 Promise-1 的 onFulfilled 没有返回值,所以 Promise-2的值为undefined。

上例中,如果把异步的请求改成同步会是什么的效果?

new Promise(resolve => {
  resolve('getUserId同步请求');
}).then(result => {
    console.log(result);
});
//打印日志
[Promse-1]:constructor
[Promse-1]:_resolve
[Promse-1]:_resolve value= getUserId同步请求
[Promse-1]:then
[Promse-2]:constructor
[Promse-1]:_handle state= fulfilled
getUserId同步请求
[Promse-2]:_resolve
[Promse-2]:_resolve value= undefined
=> Promise {
  callbacks: [],
  name: 'Promse-2',
  state: 'fulfilled',
  value: undefined }

感兴趣的可以自己去分析一下。

三、链式调用真正的意义

执行当前 Promise 的 onFulfilled 时,返回值通过调用第二个 Promise 的 resolve 方法,传递给第二个 Promise,作为第二个 Promise 的值。于是我们考虑如下Demo:

//Demo2
new Promise(resolve => {
    mockAjax('getUserId', 1, function (result) {
        resolve(result);
    })
}).then(result => {
    console.log(result);
    //对result进行第一层加工
    let exResult = '前缀:' + result;
    return exResult;
}).then(exResult => {
    console.log(exResult);
});

【 Demo2 的源码】

我们加了一层 then,来看下执行的结果:

[Promse-1]:constructor
[Promse-1]:then
[Promse-2]:constructor
[Promse-1]:_handle state= pending
[Promse-1]:_handle callbacks= [ { onFulfilled: [Function], resolve: [Function] } ]
[Promse-2]:then
[Promse-3]:constructor
[Promse-2]:_handle state= pending
[Promse-2]:_handle callbacks= [ { onFulfilled: [Function], resolve: [Function] } ]
=> Promise { callbacks: [], name: 'Promse-3', state: 'pending', value: null }
[Promse-1]:_resolve
[Promse-1]:_resolve value= getUserId异步请求耗时1秒
[Promse-1]:_handle state= fulfilled
getUserId异步请求耗时1秒
[Promse-2]:_resolve
[Promse-2]:_resolve value= 前缀:getUserId异步请求耗时1秒
[Promse-2]:_handle state= fulfilled
前缀:getUserId异步请求耗时1秒
[Promse-3]:_resolve
[Promse-3]:_resolve value= undefined:

链式调用可以无限的写下去,上一级 onFulfilled return 的值,会变成下一级 onFulfilled 的结果。可以参考Demo3:

【 Demo3 的源码】

我们很容易发现,上述 Demo3 中只有第一个是异步请求,后面都是同步的,我们完全没有必要这么链式的实现。如下一样能得到我们想要的三个结果: 分别打印出来的值。

//等价于 Demo3
new Promise(resolve => {
    mockAjax('getUserId', 1, function (result) {
        resolve(result);
    })
}).then(result => {
    console.log(result);
    //对result进行第一层加工
    let exResult = '前缀:' + result;
    console.log(exResult);
    let finalResult = exResult + ':后缀';
    console.log(finalResult);
});

那链式调用真正的意义在哪里呢?

刚才演示的都是 onFulfilled 返回值是 value 的情况,如果是一个 Promise 呢?是不是就可以通过 onFulfilled,由使用 Promise 的开发者决定后续 Promise 的状态。

于是在 _resolve 中增加对前一个 Promise onFulfilled 返回值的判断:

_resolve(value) {
        if (value && (typeof value === 'object' || typeof value === 'function')) {
            var then = value.then;
            if (typeof then === 'function') {
                then.call(value, this._resolve.bind(this));
                return;
            }
        }
        this.state = 'fulfilled';//改变状态
        this.value = value;//保存结果
        this.callbacks.forEach(callback => this._handle(callback));
    }

从代码上看,它是对 resolve 中的值作了一个特殊的判断,判断 resolve 的值是否为 Promise实例,如果是 Promise 实例,那么就把当前 Promise 实例的状态改变接口重新注册到 resolve 的值对应的 Promise 的 onFulfilled 中,也就是说当前 Promise 实例的状态要依赖 resolve 的值的 Promise 实例的状态。

分析Promise链式调用

【 Demo 4 的源码】

执行的结果如下:

[Promse-1]:constructor
[Promse-2]:constructor
[Promse-1]:then
[Promse-3]:constructor
[Promse-1]:_handle state= pending
[Promse-1]:_handle callbacks= [ { onFulfilled: [Function], resolve: [Function] } ]
[Promse-3]:then
[Promse-4]:constructor
[Promse-3]:_handle state= pending
[Promse-3]:_handle callbacks= [ { onFulfilled: [Function], resolve: [Function] } ]
=> Promise { callbacks: [], name: 'Promse-4', state: 'pending', value: null }
[Promse-1]:_resolve
[Promse-1]:_resolve value= getUserId异步请求耗时1秒
[Promse-1]:_handle state= fulfilled
getUserId异步请求耗时1秒
[Promse-3]:_resolve
[Promse-3]:_resolve value= Promise { callbacks: [], name: 'Promse-2', state: 'pending', value: null }
[Promse-2]:then
[Promse-5]:constructor
[Promse-2]:_handle state= pending
[Promse-2]:_handle callbacks= [ { onFulfilled: [Function], resolve: [Function] } ]
[Promse-2]:_resolve
[Promse-2]:_resolve value= getUserName异步请求耗时2秒
[Promse-2]:_handle state= fulfilled
[Promse-3]:_resolve
[Promse-3]:_resolve value= getUserName异步请求耗时2秒
[Promse-3]:_handle state= fulfilled
getUserName异步请求耗时2秒
[Promse-4]:_resolve
[Promse-4]:_resolve value= undefined
[Promse-5]:_resolve
[Promse-5]:_resolve value= undefined

一样的,我做了一个演示动画,还原了这个过程:

分析Promise链式调用

(Promise 真正的链式调用)

“分析Promise链式调用”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注编程网网站,小编将为大家输出更多高质量的实用文章!

--结束END--

本文标题: 分析Promise链式调用

本文链接: https://www.lsjlt.com/news/85090.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 分析Promise链式调用
    本篇内容介绍了“分析Promise链式调用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!一、前言上一节中,...
    99+
    2022-10-19
  • python链式函数调用的示例分析
    小编给大家分享一下python链式函数调用的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!链式函数调用你可以在一行代码内调用多个函数。def a...
    99+
    2023-06-27
  • Javasript设计模式之链式调用的示例分析
    这篇文章将为大家详细讲解有关Javasript设计模式之链式调用的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。具体内容如下写过jquery的可能都知道,jque...
    99+
    2022-10-19
  • zipkin微服务调用链分析(python)
    zipkin的作用在微服务架构下,一个http请求从发出到响应,中间可能经过了N多服务的调用,或者N多逻辑操作,如何监控某个服务,或者某个逻辑操作的执行情况,对分析耗时操作,性能瓶颈具有很大价值,zipkin帮助我们实现了这一监控功能。&n...
    99+
    2023-01-31
    zipkin python
  • 如何使用Promise链式调用解决多个异步回调的问题
    小编给大家分享一下如何使用Promise链式调用解决多个异步回调的问题,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!介绍所谓Promise,简单来说就是一个容器,里面保存着某个未来才会结束...
    99+
    2022-10-19
  • Netty分布式Future与Promise执行回调相关逻辑剖析
    目录Future和Promise执行回调首先我们看一段写在handler中的业务代码这里关注newPromise()方法, 跟进去我们继续跟write方法跟进tryFailure方法...
    99+
    2022-11-13
  • 分布式微服务系统下调用链追踪技术实例分析
    这篇文章主要介绍“分布式微服务系统下调用链追踪技术实例分析”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“分布式微服务系统下调用链追踪技术实例分析”文章能帮助大家解决问题。1、面试官:分布式微服务环境...
    99+
    2023-06-29
  • arthas jprofiler做复杂链路的调用分析
    目录背景1. arthas采样生成jfr文件2. 下载jfr到本地3. jprofiler分析3.1 打开快照3.2 反向分析3.3 分析结果总结背景 Arthas是阿里巴巴开源的应...
    99+
    2022-11-13
  • skywalking分布式服务调用链路追踪APM应用监控的示例分析
    这篇文章给大家分享的是有关skywalking分布式服务调用链路追踪APM应用监控的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。前言当企业应用进入分布式微服务时代,应用服务依赖会越来越多,skywalk...
    99+
    2023-06-29
  • 详解jQuery 链式调用
    目录链式调用一个小案例链式调用 jQuery对象调用任何方法(除了节点关系方法)执行完后,方法都会有一个返回值,返回值就是jQuery对象自己,这个现象给我们提供了便利,可以对执行...
    99+
    2022-11-12
  • C++链式二叉树深入分析
    目录二叉树的结构和概念二叉树的操作前序遍历中序遍历和后序遍历二叉树的节点个数求二叉树叶子结点个数求二叉树的深度在二叉树查找为X的结点之前我们的重点学习二叉树都是完全二叉树,接下来我们...
    99+
    2022-11-13
  • Java 实现分布式服务的调用链跟踪
    目录为什么要实现调用链跟踪?如何实现?第一步,看图、看场景,用户浏览器的一次请求行为所走的路径是什么样的第二步,实现。不想看代码可直接拉最后看结果和原理测试一下结果:为什么要实现调用...
    99+
    2022-11-12
  • css外部链接形式实例分析
    这篇文章主要介绍“css外部链接形式实例分析”,在日常操作中,相信很多人在css外部链接形式实例分析问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”css外部链接形式实例分析”...
    99+
    2022-10-19
  • JavaScript中链式调用之研习
    一、对象链:方法体内返回对象实例自身(this) 复制代码 代码如下: function ClassA(){ this.prop1 = null; this.prop2 = null...
    99+
    2022-11-21
    链式调用
  • Python怎么实现链式调用
    为什么是链式调用?链式调用,或者也可以称为方法链(Method Chaining),从字面意思上来说就是将一些列的操作或函数方法像链子一样穿起来的 Code 方式。我最开始感知链式调用的「美」,还要从使用 R 语言的管道操作符开始。libr...
    99+
    2023-05-15
    Python
  • Python如何实现链式调用
    本篇内容介绍了“Python如何实现链式调用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!为什么是链式调用?链式调用,或者也可以称为方法链(...
    99+
    2023-07-06
  • Java责任链模式实例代码分析
    本文小编为大家详细介绍“Java责任链模式实例代码分析”,内容详细,步骤清晰,细节处理妥当,希望这篇“Java责任链模式实例代码分析”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。优点降低耦合度。它将请求的发送者和...
    99+
    2023-06-29
  • skywalking分布式服务调用链路追踪APM应用监控
    目录前言skywalking是什么,有什么用?skywalaking总体架构分为三部分如何快速开始?第一步:进入官方release地址第二步:配置需要监控的应用的agent探针系统使...
    99+
    2022-11-13
  • 分布式系统下调用链追踪技术面试题
    目录引言1、面试官:分布式微服务环境下那么多机器,调用链又很长,你们是如何定位问题的?2、面试官:你知道哪些成熟的调用链开源工具? 总结引言 一个复杂的分布式系统,用户发起...
    99+
    2022-11-13
  • python中如何实现链式调用
    我们在使用Django的models查询数据库时,可以看到有这种写法: form app.models import XXX query ...
    99+
    2022-11-11
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作