不看后悔,async和await详解

async await详解

原理:

async声明该函数是异步的,且该函数会返回一个promise。

await必须放在async函数中使用

  • await+Promise

    这是最常见的场景,await 会等待Promise的状态改为fullfilled,如果成功,那么会将async函数剩余任务推入到微任务队列,如果失败,那么剩余任务不会被推入微任务队列执行 ,它会返回Promise.reject(err)

  • await + 普通的值

    即使await右边非函数,只是一个普通的数值,但它本质上是将其转化为 Promise.resolve(2),所以会返回一个成功的promise

    因此,当await等待到了成功的结果后,它会将async函数剩余内容推入到微任务队列中等待执行。

    js 复制代码
     async function run() {
                    console.log('start 1')
                    const res = await 2
                    console.log(res)
                    console.log('end')
                }
                run()
                console.log('3')
  • await+函数

一个最简单的场景:

js 复制代码
            function fn() {
                console.log('fn start')
                console.log('fn end')
            }
            async function run() {
                console.log('start 1')
                const res = await fn()
                console.log(res)
                console.log('end')
            }
            run()
            console.log('3')

运行结果为:

结论:如果await 右边是一个函数,它会立刻执行这个函数,而且只有当这个函数执行结束后(即函数完成)!才会将async剩余任务推入微任务队列

**这是因为await等待的永远是promise,如果函数返回undefined,那么await会等待函数执行完成,将返回值转化为Promise.resolve(undefined) **(即第二种情况 await + 普通值)。如果函数执行完成后,返回一个失败的promise,那么它将不会再把剩余任务推入到微任务队列。

复杂点的案例,如果fn函数里面嵌套了await

案例:

js 复制代码
async function async1() {
    console.log(1)
    await async2()
    console.log(2)
}

const async2 = async () => {
    await setTimeout((_) => {
        Promise.resolve().then((_) => {
            console.log(3)
        })
        console.log(4)
    }, 0)
}

const async3 = async () => {
    Promise.resolve().then(() => {
        console.log(6)
    })
}

async1()

console.log(7)

async3()

分析:

  • 首先调用async1 输出1 ,await async2()立刻调用async2(),直至async2函数完成后,才会将cl(2)推入微任务队列

  • 调用async2(), await 定时器,(定时器本身也是个函数),因此先将定时器的回调函数推入宏任务队列,定时器本身返回一个定时器ID

    因此,async2可以转化为:

    js 复制代码
    async ()=>{
    	await 数值;  //即第二种情况
    }

    重点:await 数值会转化为await Promise.resolve(数值),再将async函数中剩余任务推入到微任务队列执行。这时候,async2函数中的剩余任务还有个return undefined,这代表async2函数并不能立刻执行完毕,会将return undefined推入到微任务队列中(这才代表着async2函数真正执行结束)

目前:宏任务队列:定时器回调函数任务

​ 微任务队列:return undefined(async2函数执行完毕)

  • 回到开始,await async2(),目前async2还没有执行结束,因此调用cl(7)
  • 调用async3(),微任务队列推入 cl(6)

目前:宏任务队列:定时器回调函数任务

​ 微任务队列:return undefined(async2函数执行完毕) cl(6)

  • 从微任务队列中取出第一个任务,return undefinedasync2()函数执行完毕,await async2() 转化为 Promise.resolve(undefined),因此将cl(2) 推入微任务队列

所以真正的结果是:1 7 6 2 4 3

案例2:

js 复制代码
async function async1() {
    console.log(1)
    await async2()
    console.log(2)
}

const async2 = async () => {
    await (async () => {
        await (() => {
            console.log(3)
        })()
        console.log(4)
    })()
}

const async3 = async () => {
    Promise.resolve().then(() => {
        console.log(6)
    })
}

async1()

console.log(7)

async3()

思路:

  • 首先跟开始一样,调用async1() 输出1,遇到await async2(),进入等待状态,等待async2()函数完成

  • 调用async2函数,遇到await 立即执行函数1,立即执行立即执行函数1 ,又遇到了await 立即执行函数2,继续执行立即执行函数2,输出3。同时立即执行函数2没有返回值,等同于return undefined。因此可以转化为

    js 复制代码
    await Promise.resolve(undefined)

    等待到了成功的promise,将cl(4)和立即执行函数1的返回值结果(return undefined)即立即执行函数1完成 推入到微任务队列

目前:宏任务队列:

​ 微任务队列:cl(4) return undefined(即立即执行函数1完成)

  • 因为async2函数内部 await 立即执行函数1,所以它需要等待立即执行函数1完成后,将async2函数执行完成推入微任务队列,再将cl(2)推入微任务队列

  • 输出7

  • 调用async3 ,将cl(6)推入微任务队列

目前:宏任务队列:

​ 微任务队列:cl(4) return undefined(即立即执行函数1完成) cl(6)

  • 同步任务完成,调用微任务队列任务,输出4,立即执行函数1完成,将async2完成推入微任务队列,将cl(2)推入微任务队列。

目前:宏任务队列:

​ 微任务队列: cl(6) async2完成 cl(2)

所以最后的结果是:1 3 7 4 6 2。

相关推荐
中微子10 分钟前
JavaScript 防抖与节流:从原理到实践的完整指南
前端·javascript
天天向上102425 分钟前
Vue 配置打包后可编辑的变量
前端·javascript·vue.js
趣多多代言人26 分钟前
从零开始手写嵌入式实时操作系统
开发语言·arm开发·单片机·嵌入式硬件·面试·职场和发展·嵌入式
芬兰y41 分钟前
VUE 带有搜索功能的穿梭框(简单demo)
前端·javascript·vue.js
好果不榨汁1 小时前
qiankun 路由选择不同模式如何书写不同的配置
前端·vue.js
小蜜蜂dry1 小时前
Fetch 笔记
前端·javascript
拾光拾趣录1 小时前
列表分页中的快速翻页竞态问题
前端·javascript
小old弟1 小时前
vue3,你看setup设计详解,也是个人才
前端
Lefan1 小时前
一文了解什么是Dart
前端·flutter·dart
Patrick_Wilson1 小时前
青苔漫染待客迟
前端·设计模式·架构