博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JQuery那点事 -- Deferred(Promise的实现)
阅读量:5811 次
发布时间:2019-06-18

本文共 3197 字,大约阅读时间需要 10 分钟。

注:由于 Deferred 需要用到 Callback 因此需要先了解 Callback 的实现以及用处后才能进一步了解 Deferred 的实现及其作用。

附:

Deferred (延迟对象)解决了什么

在写 javascript 代码时,难免的回写到异步回调。

当一个回调中含有另一个回调时,假设我们要获取一个坐标点,x 需要异步获取,而 y 轴坐标需要依赖 x 轴,z 轴需要依赖 y 轴,那么写起来的代码会是这样:

var x = 10;function getX(cb) {    setTimeout(function() {        cb && cb(10);    })}// 假设对X做的操作仅仅是 +1function getY(x, cb) {    setTimeout(function() {        cb && cb(x + 1);    })}// 假设对Y做的操作仅仅是 -5function getZ(y, cb) {    setTimeout(function() {        cb && cb(y - 5);    })}getX(function(result1) {    var x = result1;    getY(x, function(result2) {        var y = result2;        getZ(y, function(result3) {            var z = result3;            var point = { x: x, y: y, z: z };            doSomeThing(point);        })    })})

这种结构有缩进看着好像也就那么回事,但是没缩进呢?

getX(function(result1){var a = result1;getY(a.x, function(result2){var b = result2;getZ(b.y, function(result3){var c = result3;var point = {x: a.x, y: b.y, z: c.z};doSomeThing(point);})})})

真是一坨啊,而且这还是仅仅3个回调,不仅语义不明确,而且可读性极低。而 Deferred 的出现就解决了这个问题。

下面就是同样的例子,使用 Deferred 实现:

var x = 10;var point = {};function getX() {    var defer = $.Deferred();    setTimeout(function() {        point.x = x;        defer.resolve(x);    })    return defer.promise();}function getY(x) {    var defer = $.Deferred();    setTimeout(function() {        point.y = x + 1;        defer.resolve(x + 1);    })    return defer.promise();}function getZ(y) {    var defer = $.Deferred();    setTimeout(function() {        point.z = y - 5;        defer.resolve(y - 5);    })    return defer.promise();}getX().then(getY).then(getZ).then(function() {    doSomeThing(point);});

逻辑写在了函数的内部,不需要更多的回调来处理数据,而且即使在缩进错误的情况下,也毫无问题。

Deferred 都做了什么?

Deferred

Deferred 的3个重要的方法在图上都显现了,下面介绍下这3个方法都做了什么

  • promise.then(fun1,fun2)

fun1 处理后添加到 resolveCallbacks 中,将 fun2 处理后添加到 rejectCallbacks 中,当 promise 状态改变时,也就是 resolve 或是 reject 方法执行后,执行对应的 Callbacks,一般 reject 处理错误数据, resolve 处理正确数据。

  • defer.resolve(obj)

通知 promise 处理 obj 数据,使用 fun1 处理

  • defer.reject(err)

通知 promise 处理 err 数据,使用 fun2 处理

简单实现

以下代码为 jQuery 3.1.0 中,关于 Deferred 源码中的一部分。经过我的一些修改,仅仅实现了主要的逻辑。

本着足够精简的态度去提取,但经过经过了一遍又一遍的考虑后,也还剩下100多行的代码。

由于代码量感觉略大,这里就不放上来了,具体的精简代码 。

其实看看还是能在精简点的,但是在精简下去就要改变 JQuery 中源码的编写方式,不利于理解 JQuery 源码了 。

具体的代码执行过程,在代码里都有,这里上个图来说明下,一个 Deferred 从创建到 then 执行,到返回新的 Deferred 的过程:

Deferred流程图

配合思维导图来看,应该更能理解:

Deferred思维导图

JQ 中的实现

当然在 JQueryDeferred 的实现远比上面的简单实现要来的复杂。但主体的逻辑和上面已经没有区别了。

下面说说主要区别有哪些地方

  1. resolvereject 各自分别实现了两个 Callbacks,这两个 Callback 其中一个用于 Deferred 内部,一个用于 then 方法调用时,这样的结构使得代码管理起来更容易。

  2. 功能性函数的添加,这个在下面一节中细说。

  3. 实现了 3 种状态,除了 resolvereject 外还实现了 notify 的状态。

  4. 处理了 Deferred 实际允许过程种,由于特殊的写法而导致的一些意料之外的 BUG

notify 的作用

notify 用于提供 Deferred 的一些状态信息,比如当前的 Deferred 的完成百分比,以及 ajax 的状态码的改变··· 但是 notify 和其他两种状态并不互斥,而其他两种的改变也不会影响 notify 状态。

功能性函数

  • .done(fun) | .fail(fun) | .progress(fun)

这3个方法分别对应 resolve | reject | notify 这3种状态下在 Deferred 内部使用的 Callbacks.add 方法,用于在相应状态时,执行我们传入的函数。相当于对外暴露了 Callbacks.add 方法。

  • .state()

用于获取 Deferred 的状态。

  • .always()

.done(arguments).fail(arguments) 的变体,表示 Deferred 变为 resolve | reject 状态都要执行的函数。

  • .catch()

.then(null, fn) 的变体,表示仅仅配置 reject 需要执行的函数。(通常用于捕获异常)

  • $.when(promises)

当传入的 promises 全部运行成功后,才调用 resolved 状态的回调函数,如果其中一个 promise 失败,就调用 rejected 状态的回调函数。

JQuery 源码(含注释)

转载地址:http://qqjbx.baihongyu.com/

你可能感兴趣的文章
开源 免费 java CMS - FreeCMS1.9 移动APP生成栏目列表数据
查看>>
git reset 三种用法总结
查看>>
hdfs笔记
查看>>
虚拟机新增加硬盘,不用重启读到新加的硬盘
查看>>
Java IO流详尽解析
查看>>
邮件服务系列之四基于虚拟用户的虚拟域的邮件系统(安装courier-authlib以及部分配置方法)...
查看>>
Linux VSFTP服务器
查看>>
DHCP中继数据包互联网周游记
查看>>
Squid 反向代理服务器配置
查看>>
Java I/O操作
查看>>
Tomcat性能调优
查看>>
项目管理心得
查看>>
Android自学--一篇文章基本掌握所有的常用View组件
查看>>
灰度图像和彩色图像
查看>>
通过vb.net 和NPOI实现对excel的读操作
查看>>
TCP segmentation offload
查看>>
java数据类型
查看>>
数据结构——串的朴素模式和KMP匹配算法
查看>>
FreeMarker-Built-ins for strings
查看>>
验证DataGridView控件的数据输入
查看>>