大白话讲解Promise(三)搞懂jquery中的Promise

687 查看

前两篇我们讲了ES6中的Promise以及Promise/A+规范,在Promise的知识体系中,jquery当然是必不可少的一环,所以本篇就来讲讲jquery中的Promise,也就是我们所知道的Deferred对象。

事实上,在此之前网上有很多文章在讲jquery Deferred对象了,但是总喜欢把ajax和Deferred混在一起讲,容易把人搞混。when、done、promise、success、error、fail、then、resolve、reject、always这么多方法不能揉在一起讲,需要把他们捋一捋,哪些是Deferred对象的方法,哪些是ajax的语法糖,我们需要心知肚明。

先讲$.Deferred

jquery用$.Deferred实现了Promise规范,$.Deferred是个什么玩意呢?还是老方法,打印出来看看,先有个直观印象:

输出如下:
$.Deferred()返回一个对象,我们可以称之为Deferred对象,上面挂着一些熟悉的方法如:done、fail、then等。jquery就是用这个Deferred对象来注册异步操作的回调函数,修改并传递异步操作的状态。
Deferred对象的基本用法如下,为了不与ajax混淆,我们依旧举setTimeout的例子:

在runAsync函数中,我们首先定义了一个def对象,然后进行一个延时操作,在2秒后调用def.resolve(),最后把def作为函数的返回。调用runAsync的时候将返回def对象,然后我们就可以.then来执行回调函数。
是不是感觉和ES6的Promise很像呢?我们来回忆一下第一篇中ES6的例子:

区别在何处一看便知。由于jquery的def对象本身就有resolve方法,所以我们在创建def对象的时候并未像ES6这样传入了一个函数参数,是空的。在后面可以直接def.resolve()这样调用。
这样也有一个弊端,因为执行runAsync()可以拿到def对象,而def对象上又有resolve方法,那么岂不是可以在外部就修改def的状态了?比如我把上面的代码修改如下:

现象会如何呢?并不会在2秒后输出“执行完成”,而是直接输出“在外部结束”。因为我们在异步操作执行完成之前,没等他自己resolve,就在外部给resolve了。这显然是有风险的,比如你定义的一个异步操作并指定好回调函数,有可能被别人给提前结束掉,你的回调函数也就不能执行了。
怎么办?jquery提供了一个promise方法,就在def对象上,他可以返回一个受限的Deferred对象,所谓受限就是没有resolve、reject等方法,无法从外部来改变他的状态,用法如下:

这样返回的对象上就没有resolve方法了,也就无法从外部改变他的状态了。这个promise名字起的有点奇葩,容易让我们搞混,其实他就是一个返回受限Deferred对象的方法,与Promise规范没有任何关系,仅仅是名字叫做promise罢了。虽然名字奇葩,但是推荐使用。

then的链式调用

既然Deferred也是Promise规范的实现者,那么其他特性也必须是支持的。链式调用的用法如下: