浅析回调函数、Promise、iterator、Generator、async-await的异步方式

想了解这些函数的异步实现,我们就首先说一下什么是异步和同步

异步和同步

异步和同步都是消息通讯机制,都是对"调用一个函数"==来说的

  • 同步:调用一旦开始,调用者必须等到被调用的函数返回了结果才会往下执行
  • 异步:调用更像一个消息传递,一旦开始,方法调用就会立即返回,调用者就可以继续后续的操作。异步方法通常会在另外一个线程中,“真实”地执行,不会阻碍调用者的工作(你可以看成多线程的形式,主函数和调用函数在并发的执行,这样比较好理解,虽然实际上不是这样

举例:你用洗衣机洗衣服
同步:站在洗衣机旁边等到衣服洗完你再晾衣服。
异步:将衣服放进洗衣机,然后你去听歌煮饭,等到洗衣机洗完的信号发出,你再回来晾衣服
同步:
在这里插入图片描述
异步:
在这里插入图片描述

异步的实现

setTimeout、Promise、iterator、Generator、async-await这些都是异步调用的方式
我理解JavaScript中的异步是,将想进行异步的操作封装成一个回调函数,再通过异步调用的方式调用这个回调函数

1.setTimeout

{
    function init() {
        console.log("1");
        setTimeout(() => {
            console.log("2");
        }, 1000);
        console.log("3");
    }
}

输出结果:1 3 2
由此可以发现setTimeout视乎有异步的功能

2.使用回调函数实现

{
    // 回调地狱
    function ajax(cb) {
        setTimeout(() => {
            cb && cb(() => {
                console.log('任务2')
            })
        }, 1000)
    }
    ajax((cb2) => {
        console.log('任务1')
        setTimeout(() => {
            cb2 && cb2()
        },1000)
    })
}

使用setTimeout实现异步请求,异步请求次数少还好,多了就陷入了回调地狱(比如:前端向后端请求查询该用户的id,再通过返回的id,请求查询用户的基本信息),这样的代码就不容易让人阅读和书写

3.Promise

为了解决回调地狱的问题,大佬们设计了Promise这种语法糖,这语法糖真香

{
    function ajax() {
        return new Promise((resolve, reject) => {
            setTimeout(() => resolve(), 1000)
        })
    }
    ajax()
        .then(() => {
            console.log('任务1')
            return new Promise(resolve => {
                setTimeout(() => resolve(), 1000)
            })
        })
        .then(() => {
            console.log('任务2')
        })
}

将想进行异步的操作封装成一个回调函数,再通过异步调用的方式调用这个回调函数,Promise里设计的成功失败函数,让代码更加灵活,也减少了代码出现卡死的情况

4.iterator

有些大佬发现iterator也有异步的功能

{
    const arr = [1, 2, 3]
    const fn = arr[Symbol.iterator]()
    console.log(fn.next())
    console.log(fn.next())
    console.log(fn.next())
    console.log(fn.next())
}

当fn调用next()函数的时候才会遍历下一个元素,这视乎也有一定的异步的功能,为了方便程序员使用,设计出了Generator和async-await两种语法糖

5.Generator

Generator就是iterator的语法糖
Generator函数就是普通的function函数后面加个*
每个yield相当于数组里的一个元素,.next()相当于移动当前数组元素的指针

模拟不断查询是否支付成功

{
    function fn1() {
        return new Promise(resolve => {
            setTimeout( () => {
                console.log('查询中')
                resolve({code: 0})
            },1000)
        })
    }

    const getStatus = function* () {
        yield fn1()
    }

    function autoGetStatus() {
        const gen = getStatus()
        const status = gen.next()
        status.value.then(res => {
            if(res.code === 0) {
                console.log('用户付款成功')
            } else {
                console.log('暂未付款')
                setTimeout( () => autoGetStatus(), 500)
            }
        })
    }

    autoGetStatus()
}

这种方式好像不是很直观,程序员又设计了一种用一种看起来像同步编程的方式来进行异步编程的语法糖async-await

6.async-await

优化版:模拟不断查询是否支付成功

{
    function findPayState() {
        return new Promise((resolve, reject)=>{
            setTimeout(() => {
                console.log('查询中');
                resolve({code: 0})
            }, 1000);
        })
    }

    async function autoGetState() {
        const json =  await findPayState()
        if(json.code === 0){
            console.log('支付成功');
        }else{
            autoGetState()
        }
    }
    autoGetState()
}

猜你喜欢

转载自blog.csdn.net/weixin_44206561/article/details/107871159