当前位置:   article > 正文

异步编程(2):async/await

async await并发2异步

异步编程

上一篇文章讲了 PromisePromise 让我们更友好的处理异步请求,尤其是在多个回调相互依赖等待的时候。但是它不是异步的最佳解决方案,所以从ES2017开始官方引入了 async 方法。下面我们来看一下。

定义

先来看一下MDN上对他的定义。

声明一个异步函数,返回的是一个Promise对象。而且在 async 函数中可以使用 await 表达式。

async 是什么

不得不说标准委员会的每个定义都是有其考虑的,名字一般都言简意赅,直明其意。从字面意思我们能知道它是异步的意思。

那他到底是怎么异步的呢。我们先看下他的返回值。

官方定义async 返回一个 Promise 对象。如果在函数中直接return 的是Promise 则直接返回。如果 return 出来的不是Promise 则对其包装成Promise。

  1. async function a1(){
  2. return 'hello world'
  3. }
  4. let result = a1()
  5. 复制代码

上面的demo 返回值 result 就是一个Promise 对象。他和其他通过 Promise 显示声明的对象是一样的,也具备三种状态,也有 then 等方法,也可以链式调用。

async 正是通过其返回Promise对象保证异步操作。

await 等待

标准中还说了,async中可以使用 await ,后面跟一个表达式。这个表达式可以是一个Promise对象或者任何其他的表达式。

那这个await的作用是什么呢?

await 会暂停当前 async 方法的执行,等待后面的Promise处理完成。这里我们要注意,await 暂停的是async 方法内的语句执行,但是不会阻塞async 方法之后的内容。

  1. async function a1(){
  2. console.log('a1 start')
  3. await new Promise((resolve,reject) => {
  4. setTimeout( () => {
  5. resolve()
  6. console.log('resolve')
  7. },1000)
  8. })
  9. console.log('a1 end ')
  10. }
  11. function a2(){
  12. console.log('this is from a2')
  13. }
  14. a1();
  15. a2();
  16. // 正确输出为:
  17. // a1 start
  18. // this is from a2
  19. // resolve
  20. // a1 end
  21. 复制代码

我们来看一下上面的demo。首先 await 确实是暂停了执行,等待 promise有了结果在执行的后面的代码,其次是,a1方法内部awati暂停的时候,不会阻塞后面a2 方法的执行。

这是为什么呢?我们都知道JS是单线程语言,异步是它的灵魂,即使 async 方法也不会改变这个事实,它依然是单线程,依然不会阻塞。

但是在 async 函数的内部,我们使用await 看起来就跟同步执行是一样的,其实这只是一个障眼法,他只是一个语法糖。

上面await 后面的表达式是一个Promise对象,那么如果是普通的表达式呢?

我们来看下:

  1. async function a1(){
  2. console.log('a1 start')
  3. await a3();
  4. console.log('a1 end ')
  5. }
  6. function a2(){
  7. console.log('this is from a2')
  8. }
  9. function a3(){
  10. console.log('a3')
  11. }
  12. a1();
  13. a2();
  14. // a1 start
  15. // a3
  16. // this is from a2
  17. // a1 end
  18. 复制代码

这次 await 后面接了一个普通的函数,我们看到await 后面的方法立即执行了,但是a1 方法还是暂停执行。也就是说无论后面跟什么表达式,await都会暂停执行async 方法。

使用姿势

我们差不多大概了解了async,具体的细节你可以去看官方文档或者其他文章,已经有很多了就不罗列了。我们现在看一下他的使用场景。

假设有这么一种场景 c依赖b,b依赖a。我们来比较下promise和 async的使用区别。

  1. // Promise
  2. function a(res){
  3. return new Promise((resolve,reject) => {
  4. setTimeout( () => {
  5. resolve(res + 100)
  6. },1000)
  7. })
  8. }
  9. function b(res){
  10. return new Promise((resolve,reject) => {
  11. setTimeout( () => {
  12. resolve(res+ 100)
  13. },1000)
  14. })
  15. }
  16. function c(res){
  17. return new Promise((resolve,reject) => {
  18. setTimeout( () => {
  19. resolve(res + 100)
  20. },1000)
  21. })
  22. }
  23. a(300).then(res => {
  24. return b(res)
  25. })
  26. .then(res => {
  27. return c(res)
  28. })
  29. .then(res => {
  30. console.log(res)
  31. })
  32. // 600
  33. 复制代码

下面我们换成async 方法。

  1. // async
  2. function a(res){
  3. return new Promise((resolve,reject) => {
  4. setTimeout( () => {
  5. resolve(res + 100)
  6. },1000)
  7. })
  8. }
  9. function b(res){
  10. return new Promise((resolve,reject) => {
  11. setTimeout( () => {
  12. resolve(res+ 100)
  13. },1000)
  14. })
  15. }
  16. function c(res){
  17. return new Promise((resolve,reject) => {
  18. setTimeout( () => {
  19. resolve(res + 100)
  20. },1000)
  21. })
  22. }
  23. async function getResult(){
  24. var res = 300;
  25. res = await a(res);
  26. res = await b(res);
  27. res = await c(res);
  28. console.log(res);
  29. }
  30. getResult()
  31. 复制代码

是不是看着更直观,更清晰了些。

另外还有个关键点要注意:

await 后面如果是异步的话,一定要是promise对象。

有两种方式,一种是上面那样自己声明,一种是跟一个 async 方法。

试想一种情景,await后面跟一个异步请求函数。如果这个函数最后返回的不是promise对象,那么他就是普通表达式。上面讲了,他会立即执行,await 表达式是拿不到你想要的异步请求的结果的。

容易误区

有一些常见的用法我们要注意下,以下为伪代码

  1. async function(){
  2. let a = await getDate1();
  3. let b = await getDate2();
  4. }
  5. 复制代码

这一段代码,语法是没问题。但是这个一般情况是b对a有依赖关系,如果没有关系应该这样写。

  1. async function(){
  2. let a = getDate1();
  3. let b = getDate2();
  4. await a;
  5. await b;
  6. }
  7. // 这样a,b不存在等待关系
  8. 复制代码

同样道理,假设以下情景,b依赖a, d依赖c。你可能会这样写

  1. // 伪代码
  2. let res1 = a();
  3. let res2 = c();
  4. await res1;
  5. b();
  6. await res2;
  7. d();
  8. 复制代码

这有个啥问题呢,就是d等待的其实是a和c耗时最长的那个。我们可以换一种方式,把他俩放在两个函数里面。

  1. // 伪代码
  2. async function ab(){
  3. await a();
  4. b()
  5. }
  6. async function cd(){
  7. await c();
  8. d()
  9. }
  10. Promise.all([ab(),cd()])
  11. 复制代码

错误处理

有时候我们可能会遇到await 等待的promise返回的是一个reject状态。那我们应该怎么处理。

  1. function a(){
  2. return new Promise((resolve,reject) => {
  3. reject('this is error')
  4. })
  5. }
  6. //第一种 trycatch 捕获
  7. async function getResult(){
  8. try {
  9. await a()
  10. } catch (error) {
  11. console.log(error)
  12. }
  13. }
  14. getResult()
  15. //第二种 通过catch 捕获
  16. async function getResult(){
  17. await a().catch(error =>{
  18. console.log(error);
  19. })
  20. }
  21. getResult()
  22. // 第三种 通过catch捕获
  23. async function getResult(){
  24. await a();
  25. }
  26. getResult().catch(error => {
  27. console.log(error)
  28. })
  29. 复制代码

好了,差不多async 大部分知识点都理清了。主要是几个关键点要注意到。希望以上对大家有一点小小的帮助,如果有不足和错误之处,望大家多提意见。

端午假期马上就过去了,明天又可以开心的搬砖了。

参考链接:

developer.mozilla.org/zh-CN/docs/…

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/555147
推荐阅读
相关标签
  

闽ICP备14008679号