异步JavaScript编程中的Promise使用方法

我在很多地方都看到过异步(Asynchronous)这个词,但在我还不是很理解这个概念的时候,却发现自己常常会被当做“已经很清楚”(* ̄? ̄)。

如果你也有类似的情况,没关系,搜索一下这个词,就可以得到大致的说明。在这里,我会对JavaScript的异步做一点额外解释。

看一下这段代码:

var start = new Date(); setTimeout(function(){ var end = new Date(); console.log("Time elapsed: ", end - start, "ms"); }, 500); while (new Date - start < 1000) {};

这段代码运行后会得到类似Time elapsed: 1013ms这样的结果。 setTimeout()所设定的在未来500ms时执行的函数,实际等了比1000ms更多的时间后才执行。

要如何解释呢?调用setTimeout()时,一个延时事件被排入队列。然后,继续执行这之后的代码,以及更后边的代码,直到没有任何代码。没有任何代码后,JavaScript线程进入空闲,此时JavaScript执行引擎才去翻看队列,在队列中找到“应该触发”的事件,然后调用这个事件的处理器(函数)。处理器执行完成后,又再返回到队列,然后查看下一个事件。

单线程的JavaScript,就是这样通过队列,以事件循环的形式工作的。所以,前面的代码中,是用while将执行引擎拖在代码运行期间长达1000ms,而在全部代码运行完回到队列前,任何事件都不会触发。这就是JavaScript的异步机制。
JavaScript的异步难题

JavaScript中的异步操作可能不总是简单易行的。

Ajax也许是我们用得最多的异步操作。以jQuery为例,发起一个Ajax请求的代码一般是这样的:

// Ajax请求示意代码 $.ajax({ url: url, data: dataObject, success: function(){}, error: function(){} });

这样的写法有什么问题吗?简单来说,不够轻便。为什么一定要在发起请求的地方,就要把success和error这些回调给写好呢?假如我的回调要做很多很多的事情,是要我想起一件事情就跑回这里添加代码吗?

再比如,我们要完成这样一件事:有4个供Ajax访问的url地址,需要先Ajax访问第1个,在第1个访问完成后,用拿到的返回数据作为参数再访问第2个,第2个访问完成后再第3个...以此到4个全部访问完成。按照这样的写法,似乎会变成这样:

$.ajax({ url: url1, success: function(data){ $.ajax({ url: url2, data: data, success: function(data){ $.ajax({ //... }); } }); } })

你一定会觉得这种称为Pyramid of Doom(金字塔厄运)的代码看起来很糟糕。习惯了直接附加回调的写法,就可能会对这种一个传递到下一个的异步事件感到无从入手。为这些回调函数分别命名并分离存放可以在形式上减少嵌套,使代码清晰,但仍然不能解决问题。

另一个常见的难点是,同时发送两个Ajax请求,然后要在两个请求都成功返回后再做一件接下来的事,想一想如果只按前面的方式在各自的调用位置去附加回调,这是不是好像也有点难办?

适于应对这些异步操作,可以让你写出更优雅代码的就是Promise。
Promise上场

Promise是什么呢?先继续以前面jQuery的Ajax请求示意代码为例,那段代码其实可以写成这个样子:

var promise = $.ajax({ url: url, data: dataObject }); promise.done(function(){}); promise.fail(function(){});

这和前面的Ajax请求示意代码是等效的。可以看到,Promise的加入使得代码形式发生了变化。Ajax请求就好像变量赋值一样,被“保存”了起来。这就是封装,封装将真正意义上让异步事件变得容易起来。
封装是有用的

Promise对象就像是一个封装好的对异步事件的引用。想要在这个异步事件完成后做点事情?给它附加回调就可以了,不管附加多少个也没问题!

jQuery的Ajax方法会返回一个Promise对象(这是jQuery1.5重点增加的特性)。如果我有do1()、do2()两个函数要在异步事件成功完成后执行,只需要这样做:

promise.done(do1); // Other code here. promise.done(do2);

这样可要自由多了,我只要保存这个Promise对象,就在写代码的任何时候,给它附加任意数量的回调,而不用管这个异步事件是在哪里发起的。这就是Promise的优势。
正式的介绍

Promise应对异步操作是如此有用,以至于发展为了CommonJS的一个规范,叫做Promises/A。Promise代表的是某一操作结束后的返回值,它有3种状态:

    肯定(fulfilled或resolved),表明该Promise的操作成功了。

    否定(rejected或failed),表明该Promise的操作失败了。

    等待(pending),还没有得到肯定或者否定的结果,进行中。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wgdwsy.html