一.概述
Promise 是异步编程的一种解决方案,比传统的解决方案—回调函数和事件—更合理和更强大。
Promise是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。
语法上,Promise 是一个对象,从它可以获取异步操作的消息。 Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。
Promise对象的特点
对象的状态不受外界影响。
Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。
一旦状态改变,就不会再变,任何时候都可以得到这个结果。
Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。
Promise的缺点
无法取消Promise,一旦新建它就会立即执行,无法中途取消。
如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。 当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
二.基本用法
Promise的几种状态
1.Pending:初始状态
2.fulfilled:成功
3.rejected:失败
处于初始状态(pending)的Promise,最终要么成功(fulfilled )要么失败(rejected ),无论成功还是失败,都要通过then方法调用相关处理程序。
Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。它们是两个函数,分别表示异步操作执行成功后的回调函数和异步操作执行失败后的回调函数。由 JavaScript 引擎提供,不用自己部署。
const promise = new Promise(function (resolve, reject) {
//...do something
if (/*异步操作成功*/) {
resolve(value);
} else {
reject(error);
}
});
Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。
promise.then(function(value){
//value
}, function(error){
//error
});
then的用法
let p1=new Promise((resolve,reject)=>{
setTimeout(()=>{//宏队列
resolve("ok");//4
},2000);
});
console.log("p1",p1);//1
let p2=p1.then((value)=>{//进入微队列
console.log("p1",p1,value);//5
return value.toUpperCase();
});
console.log("p2",p2);//2
let p3=p2.then((value)=>{//进入微队列
console.log("p2",p1,value);//6
});
console.log("p3",p3);//3
执行顺序分析:
1.首先p1的setTimeout进入宏队列,此时p1,p2,p3处于Pending状态,当时间到了之后通过resolve传入“ok”参数。
2. then方法中onfulfilled回调函数会作为新Promise对象构造时的执行器函数,且其返回值作为新Promise对象fulfilled状态下的结果,即value值。
3. 故该value值会传递给新Promise对象的then()方法中的onfulfilled回调函数,从而影响p2和p3
(同理,如果then方法中onfulfilled函数发生错误,则会将新Promise对象的状态定型为rejected。)
此时你可能会觉得这个then里面函数跟回调函数是一个道理,在异步任务完成之后执行。而Promise的优势在于,可以在then方法中继续写Promise对象并返回,然后继续调用then来进行回调操作。
实例:一个异步操作的结果返回另一个异步操作。
var p1=new Promise((resolve, reject)=>{
setTimeout(()=>reject(new Error('fail')), 3000);
});
var p2=new Promise((resolve, reject)=>{
setTimeout(()=>resolve(p1), 1000);
});
p2.then(result=>console.log(result))
.catch(error=>console.log(error));
一般,不要在then方法中定义rejected状态的回调函数,而应该总是使用catch方法。
调用resolve函数和reject函数时带有参数,那么这些参数会传递给回调函数。
reject函数的参数通常是Error对象的实例,表示抛出的错误;
resolve函数的参数除了正常的值以外,还可能是另一个Promise实例;
var p1=new Promise((resolve, reject)=>{
//...
});
var p2=new Promise((resolve, reject)=>{
//...
resolve(p1);
});
此时,p1的状态就会传递给p2。 p1的状态决定了p2的状态,如果p1为Pending,那么p2的回调函数就会等待p1的状态改变;如果p1的状态为fulfilled或rejected,那么p2的回调将会立即执行。
一般,调用resolve和reject之后,Promise的使命就完成了,后续操作都应该放到then方法里面,所以最好在它们前面加上return语句,避免意外的发生。
new Promise((resolve, reject)=>{
return resolve(1);
console.log(2); //不建议
}).then(r=>{
console.log(r);
});
all的用法
当需要将多个Promise任务一起执行时,可以使用Promise.all( )方法。
Promise.all( )实参是所有Promise实例的字面量组成的数组,执行完毕的结果是所有输出结果的所组成的数组。
例如:
var p1 = new Promise((res, rej) => {
setTimeout(() => {
res("p1");
}, 1000);
});
var p2 = new Promise((res, rej) => {
setTimeout(() => {
res("p2");
}, 2000);
});
var p3 = new Promise((res, rej) => {
setTimeout(() => {
res("p3");
}, 3000);
});
Promise.all([p1, p2, p3]).then((r) => {
console.log(r);
}).catch((err) => console.log(err.messsage));
运行结果为p1,p2,p3.
array(3)["p1","p2","p3"]是按照数组的顺序输出;若为[p3,p2,p1]则会输出[p3,p2,p1],且若数组中有一个是rejected,则最后结果输出undefined。
async/await的作用与用法
1.原理
当调用一个async函数时,会返回有一个promise对象,如果async函数中有await表达式,则会使async函数暂停执行,直到表达式中promise执行完成后继续执行await后的代码并返回
async/await 是基于promise实现的,async 函数其实就是把 promise 做了一个包装await 返回值是一个 Promise 对象,它只是把 await 后面的代码放到了 Promise.then().