异步编程
上一篇文章讲了 Promise
, Promise
让我们更友好的处理异步请求,尤其是在多个回调相互依赖等待的时候。但是它不是异步的最佳解决方案,所以从ES2017开始官方引入了 async
方法。下面我们来看一下。
定义
先来看一下MDN上对他的定义。
声明一个异步函数,返回的是一个Promise对象。而且在 async 函数中可以使用 await 表达式。
async 是什么
不得不说标准委员会的每个定义都是有其考虑的,名字一般都言简意赅,直明其意。从字面意思我们能知道它是异步的意思。
那他到底是怎么异步的呢。我们先看下他的返回值。
官方定义async 返回一个 Promise 对象。如果在函数中直接return 的是Promise 则直接返回。如果 return 出来的不是Promise 则对其包装成Promise。
- async function a1(){
- return 'hello world'
- }
-
- let result = a1()
-
- 复制代码
上面的demo 返回值 result
就是一个Promise 对象。他和其他通过 Promise
显示声明的对象是一样的,也具备三种状态,也有 then
等方法,也可以链式调用。
async 正是通过其返回Promise对象保证异步操作。
await 等待
标准中还说了,async中可以使用 await ,后面跟一个表达式。这个表达式可以是一个Promise对象或者任何其他的表达式。
那这个await的作用是什么呢?
await 会暂停当前 async 方法的执行,等待后面的Promise处理完成。这里我们要注意,await 暂停的是async 方法内的语句执行,但是不会阻塞async 方法之后的内容。
- async function a1(){
- console.log('a1 start')
- await new Promise((resolve,reject) => {
- setTimeout( () => {
- resolve()
- console.log('resolve')
- },1000)
- })
- console.log('a1 end ')
- }
-
- function a2(){
- console.log('this is from a2')
- }
-
- a1();
- a2();
-
- // 正确输出为:
- // a1 start
- // this is from a2
- // resolve
- // a1 end
- 复制代码
我们来看一下上面的demo。首先 await 确实是暂停了执行,等待 promise有了结果在执行的后面的代码,其次是,a1方法内部awati暂停的时候,不会阻塞后面a2 方法的执行。
这是为什么呢?我们都知道JS是单线程语言,异步是它的灵魂,即使 async 方法也不会改变这个事实,它依然是单线程,依然不会阻塞。
但是在 async 函数的内部,我们使用await 看起来就跟同步执行是一样的,其实这只是一个障眼法,他只是一个语法糖。
上面await 后面的表达式是一个Promise对象,那么如果是普通的表达式呢?
我们来看下:
- async function a1(){
- console.log('a1 start')
- await a3();
- console.log('a1 end ')
- }
-
- function a2(){
- console.log('this is from a2')
- }
-
- function a3(){
- console.log('a3')
- }
- a1();
- a2();
-
- // a1 start
- // a3
- // this is from a2
- // a1 end
- 复制代码
这次 await 后面接了一个普通的函数,我们看到await 后面的方法立即执行了,但是a1 方法还是暂停执行。也就是说无论后面跟什么表达式,await都会暂停执行async 方法。
使用姿势
我们差不多大概了解了async,具体的细节你可以去看官方文档或者其他文章,已经有很多了就不罗列了。我们现在看一下他的使用场景。
假设有这么一种场景 c依赖b,b依赖a。我们来比较下promise和 async的使用区别。
- // Promise
-
- function a(res){
- return new Promise((resolve,reject) => {
- setTimeout( () => {
- resolve(res + 100)
- },1000)
- })
- }
-
- function b(res){
- return new Promise((resolve,reject) => {
- setTimeout( () => {
- resolve(res+ 100)
- },1000)
- })
- }
-
- function c(res){
- return new Promise((resolve,reject) => {
- setTimeout( () => {
- resolve(res + 100)
- },1000)
- })
- }
-
- a(300).then(res => {
- return b(res)
- })
- .then(res => {
- return c(res)
- })
- .then(res => {
- console.log(res)
- })
-
- // 600
- 复制代码
下面我们换成async 方法。
- // async
-
- function a(res){
- return new Promise((resolve,reject) => {
- setTimeout( () => {
- resolve(res + 100)
- },1000)
- })
- }
-
- function b(res){
- return new Promise((resolve,reject) => {
- setTimeout( () => {
- resolve(res+ 100)
- },1000)
- })
- }
-
- function c(res){
- return new Promise((resolve,reject) => {
- setTimeout( () => {
- resolve(res + 100)
- },1000)
- })
- }
-
-
- async function getResult(){
- var res = 300;
- res = await a(res);
- res = await b(res);
- res = await c(res);
- console.log(res);
- }
-
- getResult()
-
- 复制代码
是不是看着更直观,更清晰了些。
另外还有个关键点要注意:
await 后面如果是异步的话,一定要是promise对象。
有两种方式,一种是上面那样自己声明,一种是跟一个 async 方法。
试想一种情景,await后面跟一个异步请求函数。如果这个函数最后返回的不是promise对象,那么他就是普通表达式。上面讲了,他会立即执行,await 表达式是拿不到你想要的异步请求的结果的。
容易误区
有一些常见的用法我们要注意下,以下为伪代码
- async function(){
- let a = await getDate1();
- let b = await getDate2();
- }
- 复制代码
这一段代码,语法是没问题。但是这个一般情况是b对a有依赖关系,如果没有关系应该这样写。
- async function(){
- let a = getDate1();
- let b = getDate2();
- await a;
- await b;
- }
-
- // 这样a,b不存在等待关系
- 复制代码
同样道理,假设以下情景,b依赖a, d依赖c。你可能会这样写
- // 伪代码
-
- let res1 = a();
- let res2 = c();
-
- await res1;
- b();
- await res2;
- d();
- 复制代码
这有个啥问题呢,就是d等待的其实是a和c耗时最长的那个。我们可以换一种方式,把他俩放在两个函数里面。
- // 伪代码
- async function ab(){
- await a();
- b()
- }
-
- async function cd(){
- await c();
- d()
- }
-
- Promise.all([ab(),cd()])
- 复制代码
错误处理
有时候我们可能会遇到await 等待的promise返回的是一个reject状态。那我们应该怎么处理。
-
- function a(){
- return new Promise((resolve,reject) => {
- reject('this is error')
- })
- }
-
- //第一种 trycatch 捕获
- async function getResult(){
- try {
- await a()
- } catch (error) {
- console.log(error)
- }
- }
-
- getResult()
-
- //第二种 通过catch 捕获
- async function getResult(){
- await a().catch(error =>{
- console.log(error);
- })
- }
-
- getResult()
-
- // 第三种 通过catch捕获
- async function getResult(){
- await a();
- }
-
- getResult().catch(error => {
- console.log(error)
- })
-
- 复制代码
好了,差不多async 大部分知识点都理清了。主要是几个关键点要注意到。希望以上对大家有一点小小的帮助,如果有不足和错误之处,望大家多提意见。
端午假期马上就过去了,明天又可以开心的搬砖了。
参考链接: