jQuery中的Deferred和promise

转载,来自前端乱炖,链接:http://www.html-js.com/article/1757

jQuery中的Deferred和promise?这究竟是什么东西?你可能会对这两个概念感到有些陌生。但是实际上在jQuery 1.5版本之后,Deferred和promise就是jQuery中非常常用的两个功能,你平时可能一直在使用它们却没有意识到,就好比一直在呼吸却没有注意到空气的存在一样。

让我们回到过去那段没有ipod,Xbox以及Facebook的日子。如果你想要捕捉一个鼠标点击事件,你可能需要编写这样的代码:

这样编写代码确实没错,但是当你的代码中的其他部分想要监听鼠标点击事件时,问题就出现了。在当时,这个需求完全不可能实现,因为你只能为element上的onclick属性赋值一个函数。后来我们拥有了addEventListener,于是这个问题得到了完美的解决。

现在我们对于AJAX调用存在这类似的问题。这个问题和事件没有什么关系,它的问题在于AJAX只支持一个回调函数。不仅仅是jQuery中的$.ajax()存在这个问题,底层的XMLHttpRequest对象也存在这个问题。

Promise

在jQUery之前,一个典型的$.ajax()调用如下所示:

$.ajax()调用返回了一个jQuery的XMLHttpRequest对象。到目前为止没有什么新东西。

1.5版本之后,$.ajax()的返回对象实现了CommonJS的Promises/A接口。它包含了很多的内容。CommonJS标准定义了一系列通用的独立的接口。Promises/A+是其中的一个。它带来的好处不仅仅体现在jQuery中。例如,如果你编写Node.js代码,你也可能就经常需要用到这个接口。这是一件非常棒的事情。

使用Promises处理回调函数和前面的例子有所不同:

你也可以在一个then()函数中将done()和fail()函数合并起来。我们可以将上边的代码重写为以下形式:

看到这里,你可能会问:“OK,接口确实发生了变化,但是这对于我有什么好处呢?”

使用Promises的好处有以下几点:

  • 你可以多次调用done()和fail()函数,并使用不同的回调函数。或许你的一个回调函数用来停止动画,一个用来发起一个新的AJAX请求,一个用来将接受到的数据展示给用户。

  • 即使在AJAX调用完成之后,你依然可以调用done()和fail()函数,并且回调函数可以立即执行。不同的状态之间并不会发生变量混乱。当一个AJAX调用结束时,它保持了一个成功状态或者失败状态,这个状态不会发生改变。

  • 你可以合并promises。有时你需要同时进行两个AJAX请求并且想要在两个AJAX请求都成功时调用一个函数。为了完成这个任务,你需要使用一个新的$.when()函数:

和你期待的可能有所不同,回调函数并不是接受数据作为参数,而是XHR对象。对于AJAX请求来说者非常有用,但是如果你想要在其他异步代码中使用$.when()函数,可能就有点困难。

  • 在jQuery 1.8版本之后,你可以链式的编写then()函数。在下面的代码中,在成功解析之后promise1开始运行,接着是getStuff运行,它反悔了一个promise,当这个promise成功解析时,匿名函数被执行。

每一个回调函数都接受上一个异步函数的结果,在AJAX中,指的就是返回数据。

Deferred

那么Deferred和Promise之间有什么区别呢?正如你在前面看到的,一个promise就是一个由异步函数返回的对象。当你想要自己编写一个这样的函数时你需要使用一个deferred。

一个deferred对象能做的和一个promise对象差不多,但是它有两个函数来触发done()和fail()函数。

一个deferred对象拥有一个resolve()函数来处理一个成功的结果并执行与done()相关的函数。reject()函数则用来处理失败的结果并执行与fail()相关的函数。

你可以给resolve()和reject()函数都提供参数,然后它们都将传递给与done()和fail()相关的回调函数。

promise对象没有resolve()和reject()函数。这是因为你将promise放到了其他的脚本中并且你也不想promise去resolve或者reject一个promise。

下面是一个关于deferred的简单例子。html仅仅是一个简单的拥有id属性为”result”的空div。

其中,wait()函数返回了一个promise。它将在2s之后被解析。除了setTimeout之外,异步函数中所有的东西都能这样使用,比如动画,Web worker等等。wait()函数中的代码应该很清晰,我们使用了deferred对象,但是我们返回了一个限制的promise对象。

关于jQuery中promise的争议

Domenic Denicola在他的文章《You’re Missing the Point of Promise》中批评jQuery对于Promises/A的实现方式。比如下面的例子:

如果fn1抛出一个错误,那么fail函数应该被调用。这是处理异步函数中的发生的错误的一种非常好的方式。但是这种方式并不能在jQuery中实现,Domenic Denicola认为这是jQuery在实现promise时的一个主要缺点。

如果你想使用“纯粹的”Promises/A,有几个JavaScript库可以供你选择。比如有Kristopher Kowal编写的Q。

总结

本文只是对于jQuery中的deferred对象进行了简要的介绍。jQuery支持更多的函数。如果想要了解详情,请具体查看jQuery中关于deferred的文档。


本文译自Deferred and promise in jQuery,原文地址http://www.bitstorm.org/weblog/2012-1/Deferred_and_promise_in_jQuery.html

如果你觉得本文对你有帮助,请点击下面的链接为转载来源作者提供赞助

点击这里为转载来源作者提供赞助

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注