- 作者: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()));