• 浅谈async-await


    前言

    async-await最大的特点呢就是可以让我们用同步的方式写异步代码,在讲async-await之前我们回顾一下回调函数、异步任务回调地狱

    1.回调函数

    把一个函数当作参数传递,传递的是函数的定义并不会立即执行,而是在将来特定的时机再去调用,这个函数就叫做回调函数。

    2.异步任务

    与之相对应的概念是“同步任务”,同步任务在主线程上排队执行,只有前一个任务执行完毕,才能执行下一个任务。异步任务是那些被引擎放在一边,不进入主线程、而进入任务队列的任务。前一个任务是否执行完毕不影响下一个任务的执行。

    3.回调地狱

    回顾回调任务与异步任务概念,我们知道存在异步任务的代码,是不能保证能按照顺序执行,那如果我们非要代码顺序执行呢?

            setTimeout(function () {  //第一层
                console.log('第一');
                setTimeout(function () {  //第二程
                    console.log('第二');
                    setTimeout(function () {   //第三层
                        console.log('第三');
                    }, 1000)
                }, 1000)
            }, 1000)
    

    可以看到我们用回调函数的方式,可以实现这种异步任务按顺序执行,但是呢这种回调函数嵌套回调函数,非常不雅观。其实这种写法是完全没有问题的,但是在项目中面对复杂的逻辑尤其在一个接口依赖上一个接口的情况时,代码就会显的非常臃肿,这种回调函数中嵌套回调函数的情况就叫做回调地狱。

    4 Promise

    Promise的实现可以说,解决了大半回调地狱的问题,为什么不是完美解决除了它本身有的一些缺点之外(例如:无法取消 Promise,一旦新建它就会立即执行,无法中途取消)之外,他本身的.then写法也不是特别完美,如下:

    function fn(str) {
      const res = new Promise(function (resolve, reject) {
            resolve(str);
      });
      return res;
    }
    
    fn("第一")
      .then((data) => {
        console.log(data);
        return fn("第二");
      })
      .then((data) => {
        console.log(data);
        return fn("第三");
      })
      .then((data) => {
        console.log(data);
      })
      .catch((data) => {
        console.log(data);
      });
    

    在逻辑复杂的情况下,.then的操作也似乎也没有完全脱离回调地狱的写法,看起来也不是那么美观。在这种情况下呢,于是也就有了后面的目前看来最完美的解决方案 Async-await

    async-await

    async函数,也就是我们常说的async/await,是在ES2017(ES8)引入的新特性,主要目的是为了简化使用基于Promise的API时所需的语法。可以让我们可以用近乎同步的方式写异步代码。

    如下:

    function fn(str) {
      const res = new Promise(function (resolve, reject) {
            reject("操作失败");
      });
      return res;
    }
    async function fnn() {
      const one = await fn("第一");
      const two = await fn("第二");
      const three = await fn("第三");
      console.log(one, two, three);
    }
    fnn();
    

    写起来你会发现,代码比之前要优雅许多,当然async-await也有非常多的一个规则,下面我会给大家讲一下大致的一些规则。

    一、走进Async-await规则

    规则1

    async函数返回一个 Promise 对象,可以使用then方法添加回调函数。举例说明:

    那既然async返回的是Promise对象,那么async后面的函数可以接.then()或者.catch()...嘛?我们试一试就知道了。

    打印了说明async返回的是Promise对象,并且可以接Promise的方法,并且!!!默认状态是resolved

    上面代码说明,async函数内部return语句返回的值,会成为then方法回调函数的参数

    规则2

    await命令后面是一个 Promise 对象,返回该对象的结果。如果不是 Promise 对象,就直接返回对应的值。代码说明:

    规则3

    await的使用,必须要有async。async返回的是一个Promise对象,await等待的就是这个Promise对象,所以await不能没有async(但是async可以没有await)。如果await没有async会怎么样?报错:

    有没有一种燕子没有你,我怎么活呀~的感觉

    但是现在浏览器允许顶级模块使用 await

    二、深入Async-await规则

    1、多个await时,按时序执行,相当于then

    当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。任何一个await语句后面的 Promise 对象变为reject状态,那么整个async函数都会中断执行。

    2、try…catch相当于catch

    如果希望即使前一个异步操作失败,也不要中断后面的异步操作。可将第一个await放在try...catch结构里面,这样不管这个异步操作是否成功,第二个await都会执行。

    当await后面是Promise对象的时候,我们也可直接在await后面直接.catch捕获错误

    拓展: Async-await的底层Generator函数、yield基本认知

    Generator函数其实也就是生成器函数,

    1 为了让生成器函数与普通函数做区别通常用function*声明(还有一种声明方式已被废弃)

    2 调用生成器函数不会执行函数体,而是返回一个生成器对象,这个生成器对象是一种特殊的迭代器对象,因此也是可迭代的对象

    3 我们调用迭代器的next()方法,会导致生成器函数的函数体从头开始执行,直到遇见 yield。

    4 yield(ES6新特性) 关键字用来暂停和恢复一个生成器函数,yield语句的返回一个包含属性 done(当前迭代器是否允许完毕) 和 value(yield后面的值) 的对象

    5 yield表达式本身没有返回值,或者说总是返回undefined。next方法可以带一个参数,该参数就会被当作上一个yield表达式的返回值。

    6 关闭迭代器 return方法,执行结束即 done: true

    7 Generator 函数返回的遍历器对象,都有一个throw方法,可以在函数体外抛出错误,然后在 Generator 函数体内捕获。

    遍历器对象i连续抛出两个错误。第一个错误被 Generator 函数体内的catch语句捕获。i第二次抛出错误,由于 Generator 函数内部的catch语句已经执行过了,不会再捕捉到这个错误了,所以这个错误就被抛出了 Generator 函数体,被函数体外的catch语句捕获。

  • 相关阅读:
    树和图的基础知识(洛谷)
    网络安全笔记 -- CSRF漏洞、SSRF漏洞
    docker部署MinIO集群
    mysql之索引失效场景
    C语言 switch分支结构
    (十四)devops持续集成开发——jenkins流水线使用pipeline方式发布项目
    分享:身份证读卡器java工程乱码解决办法
    二十一、C位域
    理解LLMOps: Large Language Model Operations
    linux_进程周边知识——理解冯诺依曼体系结构
  • 原文地址:https://blog.csdn.net/weixin_58207509/article/details/126255106