Promise async/await 理解和应用
Promise的作用与用法
Promise
是异步编程的一种解决方法
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
使用上更为简洁,将异步代码以同步的形式进行编写,是处理异步编程的最终也是最佳方案。