Promise async/await 理解和应用
Promise的作用与用法
Promise是异步编程的一种解决方法
- 是一种容器,保存某个未来才会结束的事件(通常是一个异步操作)的结果
- 语法上
Promise是一个对象,可以获取异步操作的消息 Promise提供统一的API,各种异步操作都可以用同样的方法进行处理
Promise对象的特点
对象的状态不受外界影响。
三种状态:
- pending(进行中)
- fulfilled(已成功)
- rejected(已失败)
一旦状态改变就不会在改变,任何时候都可以得到这个结果。
状态改变只有两个可能:
-
从
pending变为fulfilled -
从
pending变为rejected
只要出现这两个情况,状态就凝固了,不会再改变,这个时候就成为resolved(已定型)。
基本用法
ES6规定,Promise对象是一个构造函数,用来生成Promise实例。
Promise.resolve()方法
返回一个具有给定值的Promise对象。
Promise.reject()方法
返回一个具有给定值的Promise对象。
Promise实例的方法
- then()
- catch()
- finally()
then()
then()执行的回调函数会放在微任务队列中,并且注册到Promise里面。then()的返回值是Promise,如果没有返回值,相当于返回undefined。返回的Promise对象,其状态和你当前调用的resolve和reject有关。
微任务和宏任务:当执行栈清空,先执行微任务队列,待微任务队列清空,在执行宏任务队列,执行完之后,再去查看微任务队列。
catch()
catch()方法是.then(null, rejection)或.then(undefined, rejection)的别名,用于指定发生错误时的回调函数。
Promise对象的错误会有冒泡的性质,会一直向后面传递,一直大奥被捕获才停止。Promise对象抛出的错误不会传递到外层代码。
在链式调用里面catch()也会返回一个Promise,如果执行完成,没有报错状态就是fulfilled。
finally()
之前的作业里面也遇到过finally(),这个方法用于指定,不管Promise对象的最后状态是怎么样,都会执行的操作。
缺点
- 无法取消,建立马上执行
- 不设置回调参数,
Promise内部抛出错误,不会反应到外部 - 处于
pending状态,无法得知目前进度是刚开始还是即将完成
异步编程与Promise的关系
前端Js的代码是在浏览器的Js引擎中执行的,并且Js的引擎是单线程,也就是不能同时执行多个,只能一个执行完,再去执行下一个。异步的理解就是把等待请求响应的这段时间,交给Js的主线程,让它去做别的事情,等待响应回来后再去执行这个任务的后续操作。
举个简单的例子,王者荣耀马超这个英雄很需要经济,这样就可以在很多方面打出巨大优势。可以让马超在我方中路去支援的时候,吃中路线,等中路回来后,就可以再一起去上路搞事,这个是可以提高对局胜率的一种做法。
Js是怎么实现异步操作:
- 回调函数
- Promise
- async/await
回调函数的理解
顾名思义,就是回头来调用函数。
把请求的动作和响应的动作分开,然后把响应的动作单独拿出来放在一个函数里面,待执行这个任务的时候,就调用这个函数。
这里可以延伸出一个概念,回调地狱:存在异步任务的代码,不能按照顺序执行,上文已经介绍了Promise的一些基本用法,Promise是干什么的?是用来解决回调地狱而产生的,并且进行嵌套,改成链式调用。
就比如我要按照1,2,3的顺序打印,就必须设定定时器才能实现。
在这段代码,嵌套了三层,这种情况就是回调地狱,很难维护而且代码可读性是真的很差。
Promise是Js中的一个原生对象,也是几乎最佳的异步解决方案,可以替换传统的回调函数。
上述代码优化方案如下:
function fn(str){
var p=new Promise(function(resolve,reject){
//处理异步任务
var flag=true;
setTimeout(function(){
if(flag){
resolve(str)
}
else{
reject('ERROR')
}
})
})
return p;
}
fn('1')
.then((data)=>{
console.log(data);
return fn('2');
})
.then((data)=>{
console.log(data);
return fn('3')
})
.then((data)=>{
console.log(data);
})
.catch((data)=>{
console.log(data);
})
但是Promise其实还存在着另外一个问题,代码冗余很大,一眼望去都是then()...
一样是不利于维护的。
async/Await
async关键字
其作用是把关键字放在声明函数前面,告示该函数为一个异步任务,并且不会阻止后面的函数正常执行。
async function fn(){
return '1';
}
console.log(fn());
打印结果可以看得出来,他将返回的数据进行了一个封装操作,变成了一个Promise对象。和Promise一样,在执行异步任务的时候也按照成功和失败来返回不同的数据,处于成功用then,失败则使用catch接收。
async function fn() {
var flag = true;
if (flag) {
return '2';
}
else{
throw '任务处理失败'
}
}
fn()
.then(data=>{
console.log(data);
})
.catch(data=>{
console.log(data);
})
console.log('FAST RUN');
会先执行FAST RUN。
如果把flag的值改成false,则会调用catch打印任务处理失败。
await关键字
该关键字只能在async定义的函数里面使用,可以直接跟Promise对象。
function fn(str) {
var p = new Promise(function (resolve, reject) {
var flag = true;
setTimeout(function () {
if (flag) {
resolve(str)
} else {
reject('任务处理失败')
}
})
})
return p;
}
//封装一个执行上述异步任务的async函数
async function test(){
var res1=await fn('先跑到中路'); //await直接拿到fn()返回的promise的数据,并且赋值给res
var res2=await fn('吃中线');
var res3=await fn('回上路继续吃线');
console.log(res1,res2,res3);
}
await关键字如其名,等待。当async函数执行到await的时候,就开始等待在此处,不再继续往下执行,等await拿到了Promise中的resolve的数据,才会继续往下执行任务,这样保证了代码的顺序执行,也可以看着这个异步操作,更像是一个同步任务一样。
总结
promise和async/await是专门用于处理异步操作的。async/await使用上更为简洁,将异步代码以同步的形式进行编写,是处理异步编程的最终也是最佳方案。