Thunk函数 & async await 实现原理

127 阅读2分钟

thunk

参考:Thunk函数的含义和用法

Thunk 函数:传入 thunk 函数的参数是另一个函数,thunk 函数返回值也是一个函数,执行这个函数,实际上是执行了传入 thunk 函数的那个函数

eg:


function add(a,b){

    return a+b

}

function thunk(fn){

    return function(){

        let args = Array.prototype.slice.call(arguments)

        fn.apply(this,args)

    }

}

let exact = thunk(add)

exact(1,2)

add 函数可以是一个基础函数, thunk 函数中可以根据不同的需求对函数进行修改。

async await

async await 函数是对异步操作的最大集成,在 async await 之前,使用 Generator 函数对异步操作同步处理,写法是这样:


function *fun(){

yield `第一个异步操作`

yield `第二个异步操作`

return "something"

}

Generator 函数是一个状态机,封装了的多个内部状态,也可以说它是一个遍历器对象生成函数,因为执行这个函数会返回一个遍历器对象(指向内部状态的指针对象);遍历器对象里面保存着 Generator 函数当前的状态;因此要访问 Generator 函数的每个状态(执行完所有的异步方法)需要手动调用遍历器对象的 next() 方法或者使用 co 函数库作为 Generator 函数的执行器,或者自己实现一个执行器,使 Generator 函数能够自动执行。

也就是说虽然 Generator 函数可以避免 callback hell,但是也有一些附加的步骤,且要求 yield 表达式后面的表达式必须是 promise 或者是 thunk 函数

使用 async await 函数的写法大概是这样的:


async function fn(){

await `第一个异步函数`

await `第二个异步函数`

return "something"

}

看起来就像是 Generator 函数的 * 换成了 async 关键字、yield 表达式换成了 await 表达式。但是 async 函数会自动依次执行,不需要执行器,且: async 函数返回的是一个 promise 对象。

async 关键字表示这个函数里面有需要异步执行的函数,使用 async 标记的函数类似于又把函数包了一层:

eg:


async function fn(){

...一些代码...

}

相当于:


function fn(

return spawn(function* (){

})

)

这里的 spawn 函数的实现类似于下面这样:


function spawn(fn){

    return new Promise(function(resolve,reject){

    let g = fn()// 获得生成器函数的遍历器对象

    function next(fnext){

        let fResult;

        try{
            fResult = fnext()
        }catch(e) {
        return reject(e)
     }

    if(fResult.done) return resolve(fResult.value);

    Promise.resolve(fResult.value).then(function(v){
        next(()=>g.next(v))
    },function(e){// Promise.resolve 相当于 new Promise(resolve=>resolve()),不会有 reject 的情况,所以这里是捕获fResult.value执行的时候出现的异常并做处理
        next(()=>g.throw(e))
    })}

    next(()=>g.next())

    })

}

所以按照我的理解:


await abc()

//... 后面的表达式

相当于:


Promise.resolve(abc()).then("后面的表达式")

参考:

async 函数的含义和用法