由于是同步回调,会阻塞后面的代码,如果fun2是个死循环,后面的代码就不执行了。
上一小节中setTimeout就是常见的异步回调,另外常见的异步回调即ajax请求。
/* 例2.4 */ /******************异步回调******************/ function request(url, param, successFun, errorFun) { $.ajax({ type: 'GET', url: url, param: param, async: true, //默认为true,即异步请求;false为同步请求 success: successFun, error: errorFun }); } request('test.html', '', function(data) { //请求成功后的回调函数,通常是对请求回来的数据进行处理 console.log('请求成功啦, 这是返回的数据:', data); },function(error) { console.log('sorry, 请求失败了, 这是失败信息:', error); });
2.2、为什么使用Promise
说完了以上基本概念,我们就可以继续学习Promise了。
上面提到,Promise对象是用于异步操作的。既然我们可以使用异步回调来进行异步操作,为什么还要引入一个Promise新概念,还要花时间学习它呢?不要着急,下面就来谈谈Promise的过人之处。
我们先看看下面的demo,利用Promise改写例2.4的异步回调。
/* 例2.5 */ function sendRequest(url, param) { return new Promise(function (resolve, reject) { request(url, param, resolve, reject); }); } sendRequest('test.html', '').then(function(data) { //异步操作成功后的回调 console.log('请求成功啦, 这是返回的数据:', data); }, function(error) { //异步操作失败后的回调 console.log('sorry, 请求失败了, 这是失败信息:', error); });
这么一看,并没有什么区别,还比上面的异步回调复杂,得先新建Promise再定义其回调。其实,Promise的真正强大之处在于它的多重链式调用,可以避免层层嵌套回调。如果我们在第一次ajax请求后,还要用它返回的结果再次请求呢?
/* 例2.6 */ request('test1.html', '', function(data1) { console.log('第一次请求成功, 这是返回的数据:', data1); request('test2.html', data1, function (data2) { console.log('第二次请求成功, 这是返回的数据:', data2); request('test3.html', data2, function (data3) { console.log('第三次请求成功, 这是返回的数据:', data3); //request... 继续请求 }, function(error3) { console.log('第三次请求失败, 这是失败信息:', error3); }); }, function(error2) { console.log('第二次请求失败, 这是失败信息:', error2); }); }, function(error1) { console.log('第一次请求失败, 这是失败信息:', error1); });
以上出现了多层回调嵌套,有种晕头转向的感觉。这也就是我们常说的厄运回调金字塔(Pyramid of Doom),编程体验十分不好。而使用Promise,我们就可以利用then进行「链式回调」,将异步操作以同步操作的流程表示出来。
/* 例2.7 */ sendRequest('test1.html', '').then(function(data1) { console.log('第一次请求成功, 这是返回的数据:', data1); return sendRequest('test2.html', data1); }).then(function(data2) { console.log('第二次请求成功, 这是返回的数据:', data2); return sendRequest('test3.html', data2); }).then(function(data3) { console.log('第三次请求成功, 这是返回的数据:', data3); }).catch(function(error) { //用catch捕捉前面的错误 console.log('sorry, 请求失败了, 这是失败信息:', error); });
是不是明显清晰很多?孰优孰略也无需多说了吧~下面就让我们真正进入Promise的学习。
三、Promise的基本用法
3.1、基本用法
上一小节我们认识了promise长什么样,但对它用到的resolve、reject、then、catch想必还不理解。下面我们一步步学习。
Promise对象代表一个未完成、但预计将来会完成的操作。
它有以下三种状态:
pending:初始值,不是fulfilled,也不是rejected
fulfilled:代表操作成功
rejected:代表操作失败
Promise有两种状态改变的方式,既可以从pending转变为fulfilled,也可以从pending转变为rejected。一旦状态改变,就「凝固」了,会一直保持这个状态,不会再发生变化。当状态发生变化,promise.then绑定的函数就会被调用。
注意:Promise一旦新建就会「立即执行」,无法取消。这也是它的缺点之一。
下面就通过例子进一步讲解。
/* 例3.1 */ //构建Promise var promise = new Promise(function (resolve, reject) { if (/* 异步操作成功 */) { resolve(data); } else { /* 异步操作失败 */ reject(error); } });
类似构建对象,我们使用new来构建一个Promise。Promise接受一个「函数」作为参数,该函数的两个参数分别是resolve和reject。这两个函数就是就是「回调函数」,由JavaScript引擎提供。
resolve函数的作用:在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
reject函数的作用:在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
Promise实例生成以后,可以用then方法指定resolved状态和reject状态的回调函数。
/* 接例3.1 */ promise.then(onFulfilled, onRejected); promise.then(function(data) { // do something when success }, function(error) { // do something when failure });
then方法会返回一个Promise。它有两个参数,分别为Promise从pending变为fulfilled和rejected时的回调函数(第二个参数非必选)。这两个函数都接受Promise对象传出的值作为参数。
简单来说,then就是定义resolve和reject函数的,其resolve参数相当于:
function resolveFun(data) { //data为promise传出的值 }