12 August 2013

原文:JavaScript Promises

哦不,我不是在谈论使用了 Promise 后,JavaScript 就可以解决所有问题,这点连我自己也不相信。我想谈论的是一些 JavaScript 库中用到的 Promise 概念,包括 AngularJS, jQuery, DojoWinJS

Promise 是一种异步操作模式。异步操作的基本问题在于,当你开始一个异步操作后,你需要在操作完成时执行一些代码。异步代码是如此的普遍,并且大多数库也已经找到了一个解决方案:传入回调函数。并且每个库的实现有着共通之处。让我们看一个 jQuery 示例:

在上面这个例子中,你可以看到 jQuery 使用配置对象的属性 success 来指定回调函数。这不是 Promise,仅仅是一种传递回调函数的方式。当 ajax 请求完成时,它会调用函数 success。根据库对异步操作的实现,你可以传入一组回调函数(例如 success 或 failure)。然而,事实上有很多种方式可以实现这一点。

异步模式可以简化这个过程。异步操作简单地返回一个 Promise 对象,Promise 对象允许你调用一个命名为 then 的方法,来指定一些函数作为回调函数。让我们看看在 jQuery 中如何使用 Promise 对象:

有趣的是,ajax 返回的是一个实现了 Promise 模式的 xhr 对象,所以我们可以调用 then,正如你所看到的。then 的魔力在于,你可以通过调用 then 把离散的操作串联起来,并且通过调用 done 来结束操作,就像下面这样:

由于许多库开始采用 Promise 模式,对异步操作的处理将更加容易,并且不需要考虑所写的是哪种代码(例如 NodeJS、浏览器 JS 等等)。但是,如果从其他角度来看待 Promise 会是什么样子呢?

Promise 模式的一个重要关键是,方法 then 可以接受两个参数。第一个是 success 回调函数,第二个是 failure 回调函数,就像这样:

请注意,在 jQuery 中,我们可以通过调用 always 来指定无论 success 或 failure 都会执行的回调函数。

让我们看看如何使用一个 Promise 对象,下面是一个来自 AngularJS 的示例:

在 AngularJS 的实现(见变量 $q)中,首先要调用 defer()。返回的对象包含了标记成功或失败状态的方法,以及 promise。注意:在函数 _callMe 中,变量 d 通过调用 $q.defer() 创建,然后 d.promise 被返回,调用方可以继续调用 promise 方法(例如 then)。当实际的异步操作完成后(在这个例子中用 setTimeout 模拟),我们可以使用 d 的方法 resolve 来告诉 promise(调用传给方法 then 的第一个函数)。如果我们调用 reject,第二个(失败)函数将被调用。

你可以在 JSFiddle 中试验这些例子,看看会发生什么。Promise 以相当简单和出色的方式来处理异步性。我真正喜欢的是它可以简化你的代码,当必须彼此嵌入回调函数时,不会再有末日般的恐怖轮回。Promise 使这一点变得很容易。



blog comments powered by Disqus