async await函数的异步实现原理

459 阅读2分钟

async函数是generator函数的语法糖

generator函数的作用可以使函数分段执行

function* generator(){
    yield 1
    yield 2
    yield 3
}    
let gen = generator()    
gen.next()  //{ value: '1', done: false }    
gen.next()  //{ value: '2', done: false }    
gen.next()  //{ value: '3', done: false }    
gen.next()  //{ value: undefined, done: true }

因为generator函数读到yield就会停止,而next的控制权又是在我们手里,所有有没有可能我们yeild后面执行一段异步操作,在异步执行完调用next,让generator函数继续执行下去呢?

首先我们先要实现一个基础的generator自执行的执行器

function next(){
    let res = gen.next()        
    if(!res.done){            
        next()        
    }    
}    
next(gen)

然而,我们的目的是要实现yield停止的是时候执行异步函数,如下

function* generator(){
        yield readFile
        yield readFile
        yield readFile    
}

要实现异步函数的generator执行器,需要我们把next暴露给异步函数,在异步执行完后调用

function next(){
        let res = gen.next()
        if(!res.done){
            if(typeof res.value === 'function'){
                res.value(next)
            }
        }
}

这样就可以实现generator中异步函数的自执行了,但是这种异步函数是不可以传参的,于是我们还需要一个包装器

    function thunk(asyncFn){
        return function(...args){
            return function(next){
                asyncFn(args,next)
            }
        }
    }
    let thunkAsync = thunk(async)
    function* generator(){
        yield thunkAsync("file1")
        yield thunkAsync("file2")
        yield thunkAsync("file3")
    }

完整代码如下:

    function thunk(fn){
        return function(...args){
            return function(next){
                fn(...args,next)
            }
        }
    }
    function async(){
        let args = Array.prototype.slice.call(arguments)
        let next = args[args.length-1]
        args.pop()
        setTimeout(()=>{
            console.log("参数:",...args)
            next()
        },1000)
    }
    let thunkAsync = thunk(async)
    function* generator(){
        yield thunkAsync("file1")
        yield thunkAsync("file2")
        yield thunkAsync("file3")
    }
    let gen = generator()
    function next(){
        let res = gen.next()
        if(!res.done){
            if(typeof res.value === 'function'){
                res.value(next)
            }
        }
    }
    next(gen)

这还不够简便,我们开发中总是手动执行一下next会很烦,有没有可能让next方法也自动实现呢。这就需要借助promise函数,在promise中异步执行完毕执行resolve的时候隐式的调用next

promise函数的generator执行器

   function next(){
        let res = gen.next()
        if(!res.done){
            if(res.value instanceof Promise){
                res.value.then(function(val){
                    next()
                    return val
                })
            }
        }
    }

完整代码如下:

    function promiseAsync(args){
        return new Promise((resolve,reject)=>{ 
           setTimeout(()=>{
                console.log(args)
                resolve()
            },1000)
        })
    }
    function* generator(){
        yield promiseAsync("file1")
        yield promiseAsync("file2")
        yield promiseAsync("file3")
    }
    let gen = generator()
    function next(){
        let res = gen.next()
        if(!res.done){
            if(res.value instanceof Promise){ 
               res.value.then(function(val){
                    next()
                    return val
                })
            }
        }
    }
    next(gen)

总结:其实异步的实现就是暴露next给外部,在异步结束的时候调用,如果是promise函数,可以在resolve执行时隐式调用。在上篇文章中介绍的异步队列的执行由很多共同点,感兴趣的可以学习交流下juejin.cn/post/684490…