异步是ES6中使用频率最高的特性之一,也是面试中经常会被问到的问题,特此整理了相应的笔记
一、Promise的三种状态
pending-异步操作没有结束
fulfilled-异步操作已成功结束,最常见的就是在promise对象中返回resolve()
rejected-异步操作未成功,可能是有错误等,最常见的就是在promise对象中返回reject()
二、Promise的几大特性
1.立即执行特性
当实例化了一个Promise对象时,作为Promise参数传入的函数是会被立即执行的,只是其中执行的代码可以是异步代码。
特别需要注意的是,不能理解为调用.then()时Promise参数中的函数代码才开始执行
var p = new Promise(function(resolve, reject){ console.log("create a promise"); resolve("success"); }); p.then(function(value){ console.log(value); });
console.log("after new Promise");
控制台输出结果
"create a promise"
"after new Promise"
"success"
var p2 = new Promise(function(resolve, reject){ resolve("success"); resolve("suceess2"); });
p2.then(
function
(value){
console.log(value);
});
控制台输出的结果是sucess而不是sucess2
4.链式调用
- Promise对象出现的目的就是避免回调嵌套,避免回调嵌套的手段就是使用链式调用。链式调用的实现原理实现中很简单,就是在每个方法中返回一个相同类型的对象。
- 不管是then()还是catch,不管使不使用return最终都会返回一个新的Promise实例。
- 没有return时默认返回一个状态为fulfilled的对象,如果在then中抛出异常则返回一个rejected状态的对象;
- 使用return时可以返回一个指定状态的Promise实例,也可以返回为普通值,当返回的是普通值时,实际上JS引擎会进行包装
var p = new Promise(function(resolve, reject){ resolve(1); }); p.then(function(value){ //第一个then console.log(value); return value*2; }).then(function(value){ //第二个then console.log(value); }).then(function(value){ //第三个then console.log(value); return Promise.resolve('resolve'); }).then(function(value){ //第四个then console.log(value); return Promise.reject('reject'); }).then(function(value){ //第五个then console.log('resolve: '+ value); }, function(err){ console.log('reject: ' + err); })
- then中的Promise,必须在then内部再次使用then或catch进行处理,不然外层的then或catch无法捕获到状态
var p = new Promise(function(resolve, reject){ setTimeout(resolve('我是在then里调用的'),50) }); var p2=function(){ return new Promise(function(resolve, reject){ setTimeout(resolve('放在第一个then中执行'),50) }); } p.then(function(value){ console.log('第一个then') p2() }) .then(()=>{ console.log('第二个then语句') }) .catch((e)=>{ console.log(e) })
上面的代码在p的第一个then中又调用了一个Promise-p2,此时p2没有没用then()或catch(),因此该then方法直接把控制权转给了后面的then或catch,控制台输出的结果如下
---第一个then
---第二个then
当我们把p2()改成p2().then((e)=>console.log(e))时,最外面一层的第二个then会进行等待:
---第一个then
---放在第一个then中执行
---第二个then
三、resolve和reject中的返回值是Promise类型对象时的处理机制
既可以在执行器中使用resolve(xxx)和reject(xxx),也可以使用Promise.resolve(xxx)和Promise.reject(xxx)来返回对象的执行状态,如果返回值是一个Promise类型的对象时情况比较有趣。
如果xxx是一个Promise类型,resolve(xxx)或Promise.resolve(xxx)会等待xxx的执行状态,并把XXX返回的状态做为对象的最终状态。
var p2 = new Promise(function(resolve, reject){ resolve(Promise.reject('reject')); //resolve中是一个执行失败的状态,因此p2的状态为rejected }); p2.catch((err)=>console.log(err))
reject(xxx)或Promise.reject(xxx)则相对简单粗暴,直接把对象的状态变为rejected,不会根据XXX的执行结果来改变自身状态
var p2 = new Promise(function(resolve, reject){ reject(Promise.resolve('resolve')); }); p2.catch((e)=>{ console.log(e) }) var p3=Promise.reject(Promise.resolve('成功状态')) p3.catch((e)=>{ console.log(e) })
四、Promise中的错误
1.既可以使用throw语句,也可以使用reject,代码如下:
var p = new Promise(function(resolve, reject){ throw new Error('显示抛出一个错误') //上面的代码等同于 try{ throw new Error('显示抛出一个错误') } catch(ex){ reject(ex) } });
2.抛出的错误可以使用then(null,(err)=>{...})也可以使用.catch((err)=>{})来捕获,当使用链式调用时,后面的catch可以捕获前面的错误
var p = new Promise(function(resolve, reject){ throw new Error('显示抛出一个错误') }); //异常处理 p.then(null,function(err){ console.log(err.message); return Promise.reject('返回一个reject') }) .catch((err)=>{console.log(err)})
3.es2018新增了.finally(),不管Promise实例最终返回什么状态,都可以被该方法捕获到
promise .then(result => {···}) .catch(error => {···}) .finally(() => {···});
4.错误处理钩子
未使用上面的错误处理机制时,可以监听unhandledrejection事件和rejectionhandled事件来获取错误
unhandledRejection :当一个 Promise 被拒绝、而在事件循环的一个轮次中没有任何拒 绝处理函数被调用,该事件就会被触发;
rejectionHandled :若一个 Promise 被拒绝、并在事件循环的一个轮次之后再有拒绝处 理函数被调用,该事件就会被触发。
在node.js中使用process.on('xxxx', (reason, promise) => ···)来监听事件,在window中使用window.addEventListener('unhandledrejection', event =>...),浏览器的event对象有三个属性
type : 事件的名称( "unhandledrejection" 或 "rejectionhandled" );
promise :被拒绝的 Promise 对象;
reason : Promise 中的拒绝值(拒绝原因)。