参考文献:《JavaScript重难点实例精讲》周雄
1.诞生背景
Promise诞生以前,在处理一个异步请求时,我们通常是在回调函数中做处理.
$.ajax({
url:'url1',
success:function(){
//回调函数
}
});
假如在一个行为中,需要执行多个异步请求,每一个请求又需要依赖上一个请求的结果,按照回调函数的处理方法,代码如下所示。
//第一个请求
$.ajax({
url:'url1',
success:function(){
//第二个请求
$.ajax({
url:'url2',
success:function(){
//第三个请求
$.ajax({
url:'url3',
success:function(){
//成功回调
}
})
}
})
}
})
一个行为所产生的异步请求可能比这个还要多,这就会导致代码的嵌套太深,引发“回调地狱”。
“回调地狱”存在以下几个问题:
(1)代码臃肿,可读性差。
(2)代码耦合度高,可维护性差,难以复用。
(3)回调函数都是匿名函数,不方便调试。
为了解决回调地狱,Promise应运而生。
2.Promise的生命周期
每一个Promise对象都有3种状态:pending(进行中)、fulfilled(已成功)、rejected(已失败)。
创建成功时处于pending状态,状态的改变只有两种可能。pending转fulfilled或者pending转reject。
状态一旦改变,就不能再改变。
3 Promise的基本用法
(1)Promise对象本身是一个构造函数,可以通过new操作符生成Promise的实例。
const promise = new Promise((resolve,reject)=>{
//异步请求处理
if(/异步请求标识/){
//判断结果为true,异步请求执行成功,调用resolve()函数。
//resolve一旦执行,promise的状态就从pending变为fulfilled
resolve();
}else{
//判断结果为false,异步请求执行失败,调用reject()函数
//reject一旦执行,promise的状态就从pending变为rejected
reject();
}
});
resolve()函数和reject()函数可以传递参数,作为后续.then()函数或者.catch()函数执行时的数据源。
(2)注意:Promise在创建后会立即调用,然后等待执行resolve()函数或者reject()函数来确定Promise的最终状态。
let promise = new Promise(function(resolve,reject){
console.log('Promise');
resolve();
});
promise.then(function(){
console.log('resolved');
});
console.log('Hello');
//先后输出'Promise','Hello','resolved'
//输出Promise,执行resolve()函数,触发then()函数指定回调函数的执行,但它需要等当前线程中的所有同步代码执行完毕,因此会先执行最后一行同步代码,输出“Hello”
(3)当一个Promise实例创建好后,使用then()函数和catch()函数进行成功和失败的异步处理。
(3.1)then()函数,表示在Promise实例状态改变时执行的回调函数。
解决上面“回调地狱”:
const promise = new Promise((resolve,reject)=>{
resolve(1);
});
//then()函数链式调用
promise.then((result)=>{
console.log(result);//1
return 2;
}).then((result)=>{
console.log(result);//2
return 3;
}).then((result)=>{
console.log(result);//3
})
注意:在then()函数中不能返回Promise实例本身,否则会出现Promise循环引用的问题,抛出异常。
const promise = Promise.resolve()
.then(()=>{
return promise; //TypeError:Chaining cycle detected for promise #<Promise>
});
虽然then()函数能够处理rejected状态的Promise的回调函数,但是并不推荐这么做,推荐catch()函数处理。
(3.2)catch()函数,与then()函数是成对存在的,then()函数是Promise执行成功之后的回调,而catch()函数是Promise执行失败之后的回调,它所接收的参数就是执行reject()函数时传递的参数。
使用try...catch
const promise = new Promise((resolve,reject)=>{
try{
throw new Error('test');
}catch(err){
reject(err);
}
});
promise.catch((err)=>{
console.log(err);//Error:test
});
实际只要在Promise执行过程中出现异常,就会自动抛出,并触发reject(err),以上等同于以下功能。
const promise = new Promise((resolve,reject)=>{
throw new Error('test');
});
promise.catch((err)=>{
console.log(err);//Error:test
});
空指针测试异常:
在Promise接收的函数体中引用null的name属性时,会抛出一个异常。
const promise = new Promise((resolve,reject)=>{
null.name;
});
promise.catch((err)=>{
console.log(err);//TypeError:Cannot read property 'name' of null
});
如果一个Promise的状态已经变成fulfilled成功状态,再去抛出异常,是无法触发catch()函数的。状态一旦改变,永久保持。
const promise = new Promise((resolve,reject)=>{
resolve(1);
throw new Error('test');
});
Promise
.then((result)=>{
console.log(result);//1
})
.catch((err)=>{
console.log(err);
})
(4)Promise静态函数
(4.1)Promise.all()函数,将多个Promise实例包装成一个新的Promise实例。
const p = Promise.all([p1,p2,p3]);
返回的新Promise实例p的状态由3个Promise实例p1、p2、p3共同决定,会有2种情况:
*1.只有p1、p2、p3全部的状态都变为fulfilled成功状态,p的状态才会变为fulfilled状态,此时p1、p2、p3的返回值组成一个数组,作为p的then()函数的回调函数的参数。
*2.只要p1、p2、p3中有任意一个状态变为rejected失败状态,p的状态就变为rejected状态,此时第一个被reject的实例的返回值会作为p的catch()函数的回调函数的参数。
注意:作为参数的Promise实例p1、p2、p3,如果已经定义了catch()函数,那么当其中一个Promise状态变为rejected时,并不会触发Promise.all()函数的catch()函数。
const p1 = new Promise((resolve,reject)=>{
resolve('success');
})
.then(result=>result)
.catch(e=>e);
const p2 = new Promise((resolve,reject)=>{
throw new Error('error');
})
.then(result=>result)
.catch(e=>e);
Promise.all([p1,p2])
.then(result=>console.log(result))//['success',Error:error]
.catch(e=>console.log(e));
如果想要Promise.all()函数能触发catch()函数,那么就不要在p1、p2实例中定义catch()函数。
const p1 = new Promise((resolve,reject)=>{
resolve('success');
})
.then(result=>result);
const p2 = new Promise((resolve,reject)=>{
throw new Error('error');
})
.then(result=>result);
Promise.all([p1,p2])
.then(result=>console.log(result))
.catch(e=>console.log(e));//抛出异常,Error:error
(4.2)Promise.race()函数
Promise.all()函数作用于多个Promise实例上,返回一个新的Promise实例,表示的是如果多个Promise实例中有任何一个实例的状态发生改变,那么这个新实例的状态就随之改变,而最先改变的那个Promise实例的返回值将作为新实例的回调函数的参数。
const p = Promise.race([p1,p2,p3]);
当p1、p2、p3这3个Promise实例中有任何一个执行成功或者失败时,由Promise.race()函数生成的实例p的状态就与之保持一致,并且最先那个执行完的实例的返回值将会成为p的回调函数的参数。
(4.3)Promise.resolve()函数
它等价于在Promise函数体内调用resolve()函数。
*1.Promise.resolve()函数执行后,Promise的状态会立即变为fulfilled,然后进入then()函数中做处理。
Promise.resolve('hello');
//等价于
new Promise(resolve=>resolve('hello'));
*2.在Promise.resolve(param)函数中传递的参数param,会作为后续then()函数的回调函数接收的参数。
Promise.resolve('success').then(result=>console.log(result));//success
(4.4)Promise.reject()函数
等价于在Promise函数体内调用reject()函数。
const p = Promise.reject('出错了');
//等价于
const p = new Promise((resolve,reject)=>reject('出错了'));
在Promise. reject (param)函数中传递的参数param,会作为后续catch()函数的回调函数接收的参数。
Promise.reject('fail').catch(result=>console.log(result));//fail