• 手写async await核心原理,再也不怕面试官问我async await原理


    前言

    async await 语法是 ES7出现的,是基于ES6的 promise和generator实现的

    generator函数

    在之前我专门讲个generator的使用与原理实现,大家没了解过的可以先看那个手写generator核心原理,再也不怕面试官问我generator原理

    这里就不再赘述generator,专门的文章讲专门的内容。

    await在等待什么

    我们先看看下面这代码,这是async await的最简单使用,await后面返回的是一个Promise对象:

    async function getResult() {
        await new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve(1);
                console.log(1);
            }, 1000);
        })
    }
    
    getResult()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    但不知你有没有想过一个问题,为什么会等到返回的promise的对象的状态为非pending的时候才会继续往下执行,也就是resolve执行之后,才会继续执行,就像下面的代码一样

    async function getResult() {
        await new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve(1);
                console.log(1);
            }, 1000);
        })
        console.log(2);
    }
    
    getResult()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    可以看到运行结果是先打印了1,再打印2了,也就是说明在返回的promise对象没执行resolve()前,就一直在await,等它执行。然后再执行下面的程序,那这个是怎么实现的呢?

    原理实现

    我们看一下下面的代码,输出顺序是什么?

    async function getResult() {
        await new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve(1);
                console.log(1);
            }, 1000);
        })
        
    
        await new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve(2);
                console.log(2);
            }, 500);
        })
    
        await new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve(3);
                console.log(3);
            }, 100);
        })
    
    }
    
    getResult()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    没错是 1,2,3.

    那用generator函数专门来实现这个效果呢

    我一开始这样来实现:

    function* getResult(params) {
        
        yield new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve(1);
                console.log(1);
            }, 1000);
        })
    
        yield new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve(2);
                console.log(2);
            }, 500);
        })
    
        yield new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve(3);
                console.log(3);
            }, 100);
        })
    }
    const gen = getResult()
    
    gen.next();
    gen.next();
    gen.next();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    但是发现打印顺序是 3,2,1.明显不对。

    这里的问题主要是三个 new Promise几乎是同一时刻执行了。才会出现这种问题,所以需要等第一个promise执行完resolve之再执行下一个,所以要这么实现

    function* getResult(params) {
    
        yield new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve(1);
                console.log(1);
            }, 1000);
        })
    
        yield new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve(2);
                console.log(2);
            }, 500);
        })
    
        yield new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve(3);
                console.log(3);
            }, 100);
        })
    }
    const gen = getResult()
    
    gen.next().value.then(() => {
        gen.next().value.then(() => {
            gen.next();
        });
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30


    可以看到这样就打印正常了。

    特别 需要解释下。gen.next().value 就是返回的promise对象,不理解的可以看看文首介绍的那篇generator的 文章。手写generator核心原理,再也不怕面试官问我generator原理

    优化

    但是呢,总不能有多少个await,就要自己写多少个嵌套吧,所以还是需要封装一个函数,显然,递归实现最简单

    function* getResult(params) {
    
        yield new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve(1);
                console.log(1);
            }, 1000);
        })
    
        yield new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve(2);
                console.log(2);
            }, 500);
        })
    
        yield new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve(3);
                console.log(3);
            }, 100);
        })
    }
    const gen = getResult()
    
    function co(g) {
        g.next().value.then(()=>{
            co(g)
        })
    }
    
    co(gen)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    再来看看打印结果

    可以发现成功执行了,但是为什么报错了?

    这是因为generator方法会返回四次,最后一次的value是undefined。

    而实际上返回第三次就表示已经返回done,代表结束了,所以,我们需要判断是否是已经done了,不再让它继续递归

    所以可以改成这样

    function* getResult(params) {
    
        yield new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve(1);
                console.log(1);
            }, 1000);
        })
    
        yield new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve(2);
                console.log(2);
            }, 500);
        })
    
        yield new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve(3);
                console.log(3);
            }, 100);
        })
    }
    const gen = getResult()
    
    function co(g) {
        const nextObj = g.next();
        if (nextObj.done) {
            return;
        }
        nextObj.value.then(()=>{
            co(g)
        })
    }
    
    co(gen)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    可以看到这样就实现了。

    完美,这个co其实也是大名鼎鼎的co函数的简单写法

    本篇文章关于async 和 await的原理揭秘就到此为止了,再讲下去就不礼貌了。

    更多文章,可以去我的github看看

    好文推荐

    这是我的github,欢迎大家star:https://github.com/Sunny-lucking/blog

  • 相关阅读:
    七个合法学习黑客的网站,让你从萌新变为大佬!
    OpenFeign开启Hystrix时,实现服务间并且调用时传递header以及新增header, header透传
    单臂路由实验:通过Trunk和子接口实现VLAN互通
    OSPF高级特性——控制OSPF路由信息
    ROS2 从头开始​​:第6部分 - ROS2 中的 DDS,用于可靠的机器人通信
    机器学习第十四课--神经网络
    java之NIO编程
    【李航统计学习笔记】第二章:感知机
    Java项目:JSP旅游产品销售管理系统
    如何获取standard cell各端口的作用
  • 原文地址:https://blog.csdn.net/weixin_43964148/article/details/126557091