- 作者:Leo晓华
回调地狱
由于 JavaScript 的单线程性质,我们必须等待上一个请求返回结果后才能处理下一步,如下:
$.ajax({ url: "/step1", success: function(){ $.ajax({ url: "/step2", success: function(){ $.ajax({ url: "/step3", success: function(){ } }); } }); }});复制代码
这种回调地狱嵌套层级多了,代码结构就容易变得很不直观,可读性比较差。
Promise 是 ES6原生支持的,把原来嵌套的回调改为了级联的方式。了解Promise
Promise 可以简单理解为一个事务,这个事务存在三种状态:
- 已经完成了 resolved(完成态)
- 因为某种原因被中断了 rejected(失败态)
- 初始状态 pending(未完成)
注意,这种状态的改变只会出现从未完成态向完成态或失败态转化,不能逆反。完成态和失败态不能互相转化,而且,状态一旦转化,将不能更改。
只有异步操作的结果可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思是承诺,表示其他手段无法改变。
例子:
var p = new Promise(function (resolve, reject) { if(/* 异步操作成功 */){ resolve(ret); } else { reject(error); }});复制代码
在声明一个Promise对象实例时,我们传入的匿名函数参数中:
resolve 就对应着完成态之后的操作 reject 对应着失败态之后的操作Promise的then方法
p.then(function (value) { // 完成态,value是上面resolve传入的值}, function (error) { // 失败态,error是上面reject传入的值});复制代码
then()方法传递的两个参数中:
-
第一个参数(函数)对应着完成态的操作,也就是resolve时调用
-
第二个参数(函数)对应着失败态的操作,也就是reject时调用
-
第二个参数可以没有
多个promise链式
例子1:
var p1 = new Promise((resolve, reject) => { setTimeout(() => resolve('p1'), 1000);});p1.then( ret => { console.log(ret); //p1 return'then1';}).then( ret => { console.log(ret); //then1 return'then2';}).then( ret => { console.log(ret); //then2});复制代码
在 resolve 之前,promise 的每一个 then 都会将回调函数压入队列,resolve 后,将 resolve 的值送给队列的第一个函数,第一个函数执行完毕后,将执行结果再送入下一个函数,依次执行完队列。一连串下来,一气呵成,没有丝毫间断。
链式中的then方法(第二个开始),它们的resolve中的参数是什么?答案就是前一个then()中resolve的return语句的返回值。例子2:
step1:function(){ var promise = new Promise(function(resolve,reject){ $.ajax({ url:"/step1", success:function(data){ resolve(data)//在异步操作成功时调用 } }); }) return promise; }, step2:function(val){ var promise = new Promise(function(resolve,reject){ $.ajax({ url:"/step2", data:val, //来自step1的参数 success:function(data){ resolve(data)//在异步操作成功时调用 } }); }) return promise; } step1() .then(data => return step2(data)) //step1的结果传给step2作为参数 .then(data => console.log(data))复制代码
错误处理
方法1:由then的第二个处理函数处理错误
var p = new Promise(function (resolve, reject) { // ... if(/* 异步操作成功 */){ resolve(ret); } else { reject(error); }});p.then(function (value) { // 完成态}, function (error) { // 失败态});复制代码
方法2:链式错误处理 (catch 方法)
var p = new Promise(function (resolve, reject) { // ... if(/* 异步操作成功 */){ resolve(ret); } else { reject(error); }});p.then(function (value) { // 完成态}).then(function (value) { // 完成态}).catch( err => {// 可以捕抓到前面的出现的错误。 console.log(err.toString());});复制代码
如果第一个then报错,第二个then不会执行。
一个活动项目的例子:
//查用户信息querymystate(){ var promise = new Promise(function(resolve,reject){ utils.get('/querystate', { raffle_code:xxx, }, (rst) => { if (rst.return_code === 0) { //成功获取用户信息 resolve(rst.data); } else { //获取失败 reject(rst.error_message); } }) }) return promise;},//我要领奖,必须先获取用户信息getAward(){ querymystate() .then(data => { //领奖处理 }).catch(ErrMsg => { //弹出错误提示ErrMsg })}复制代码
Promise.all()
Promise.all()方法用于将多个Promise实例,包装成一个新的Promise实例,例如:
var p = Promise.all([p1, p2, p3]);复制代码
新的Promise实例p的状态由p1, p2, p3决定:
-
当p1, p2, p3的状态都为完成态时,p为完成态。
-
p1, p2, p3中任一一个状态为失败态,则p为失败态。
例子:
let a = new Promise((resolve, reject) => { setTimeout(() => { resolve(2) }, 2000) }) let b = new Promise((resolve, reject) => { setTimeout(() => { resolve(3) }, 2000) }) Promise.all([a, b]).then( (ret) => console.log(ret)) //2秒后,注意这里返回的是数组 [2,3].catch( err => console.log(err.toString()));复制代码
Promise.race()
race意思是赛跑,看谁先到。只要p1, p2, p3中任意一个实例率先改变状态,则p的状态就跟着改变,而且状态由率先改变的实例决定。
let a = new Promise((resolve, reject) => { setTimeout(() => { resolve(2) }, 3000) }) let b = new Promise((resolve, reject) => { setTimeout(() => { resolve(3) }, 2000) }) Promise.race([a, b]).then( (ret) => console.log(ret)) //2秒后显示3.catch( err => console.log(err.toString()));复制代码