美文网首页es6
详解generator(三)——处理thunk

详解generator(三)——处理thunk

作者: mytac | 来源:发表于2018-05-09 08:59 被阅读36次

    thunk

    js中的thunk是指一个用于调用另外一个函数的函数,没有任何参数。也可以说,使用一个函数定义封装函数调用,包括需要的任何参数,来定义这个调用的执行,那么这个封装函数就是一个thunk。如:

    function foo(x,y){
        return x+y
    }
    
    function bar(){
        foo(1,2)
    }
    
    console.log(bar()) // 3
    

    以上,同步的thunk是非常简单的。

    异步thunk

    把狭窄的thunk定义扩展到包含接受一个回调。

    function bar(a,b,cb){
        setTimeout(function(){
            cb(a+b)
        },1000)
    }
    
    function barThunk(cb){
        bar(1,2,cb)
    }
    
    barThunk((a)=>{console.log(a)}) // 3
    

    thunkify

    如上所见,barThunk只需一个传入回调函数参数cb,因为它已经预先有指定值1和2可以传给bar。thunk只需等待回调完成。

    但是我们并不会给每个函数都写一个他自己的thunk函数,再在这个函数中去调用原函数,所以来封装一个thunkify工具来完成这项工作。

    function thunkify(fn){
        const args=[].slice.call(arguments,1)
        return function(cb){
            args.push(cb)
            fn.apply(null,args)
        }
    }
    
    const barThunk=thunkify(bar,1,2)
    
    barThunk((sum)=>{console.log(sum)})
    

    然而,以上方式实现thunkify需要传入bar()函数和其他参数,每次调用这个thunkify需要:

    const barThunk1=thunkify(bar,1,2)
    const barThunk2=thunkify(bar,3,4)
    
    barThunk1((sum)=>{console.log(sum)})
    barThunk2((sum)=>{console.log(sum)})
    

    是不是略显臃肿,且不灵活;我们再把thunkify重构一下,使其构造一个工厂函数thunkory(thunk + factory),再由thunkory生成thunk。

    function thunkify(fn) {
        return function() {
            const args = [].slice.call(arguments)
            return function(cb) {
                args.push(cb)
                fn.apply(null, args)
            }
        }
    }
    
    const barThunkory=thunkify(bar)
    
    const barThunk1=barThunkory(1,2)
    const barThunk2=barThunkory(3,4)
    
    barThunk1((sum)=>{console.log(sum)})
    barThunk2((sum)=>{console.log(sum)})
    
    ----
    3 7 同时输出
    

    promise/thunk

    thunk和promise的特性并不相同,promise要比裸thunk功能更强、更值得信任。

    但从另一个角度来说,他们都可以被看作是对一个值的请求,回答值可能是异步的。

    thunkory产生的函数是thunkify;thunkify产生thunk;而promisory产生promisify,接着产生promise。所以thunkory和promisory是完全对称的。

    为了说明这种对称性,改变一下前面的bar()

    function bar(a,b,cb){
        setTimeout(function(){
            cb(null,a+b)
        },1000)
    }
    
    
    const barThunkory=thunkify(bar)
        const barPromisory=promisify(bar)
    
    const barThunk=barThunkory(1,2)
    const barPromise=barPromisory(1,2)
    
    barThunk1((err, sum) => {
        if (err) {
            console.error(err)
        } else {
            console.log(sum)
        }
    })
    fooPromise
        .then(sum => {
                console.log(sum)
            },
            err => {
                console.error(err)
            }
        )
    

    thunkory和promisory本质上都在提出一个请求,分别由thunk fooThunk和promise fooPromise表示对这个请求的未来的答复。

    我们更改一下上一篇文章中的run函数,使其可以自动yield thunk。

    function run(gen) {
        const args = [].slice.call(arguments, 1)
        const it = gen.apply(this, args)
    
        return Promise.resolve() //创建一个已完成的promise
            .then(function handleNext(value) {
                const next = it.next(value)
                return (function handleResult(next) {
                    if (next.done) { // 生成器执行完成
                        return next.value
                    }else if(typeof next.value==='function'){
                        return new Promise((resolve,reject)=>{
                            next.value((err,msg)=>{
                                if(err){reject(err)}
                                    else{
                                        resolve(msg)
                                    }
                            })
                        }).then(handleNext,
                        function handleErr(err) {
                                    return Promise.resolve(it.throw(err)).then(handleResult)
                                }
                        )
                    } 
                    else { // 继续执行
                        return Promise.resolve(next.value).then(handleNext,handleErr)
                    }
                })(next)
            })
    }
    

    我们尝试run一个含有thunkory的generator:

    function *baz(){
        console.log(yield barThunkory(1,2))
        console.log(yield barThunkory(3,4))
    }
    
    run(baz)
    ------
    3
    7
    

    ES6之前的生成器

    对于es6中所有的语法扩展来说,都有工具用于接收es6语法并将其翻译为等价的前ES6代码~

    手工变换

    在实现transpiler之前,我们先推导下对于生成器来说手工变换是如何实现的。

    先写一个generator例子:

    function* foo(url) {
        // state1
        try {
        // 这里request是一个支持promise的ajax工具
            console.log('url is ', url)
            const TMP1= request(url)
            
            // state2
            const r = yield TMP1
            console.log(r)
        } catch (err) {
            //state3
            console.error(err)
            return false
        }
    }
    
    const it=foo('http://some.url.1')
    

    1.构造迭代器轮廓

    我们来构造一个迭代器先把轮廓写出来迭代器包含next和throw方法

    function foo(url){
        return {
            next:(v)=>{},
            throw:(e)=>{}
        }
    }
    
    const it=foo('http://some.url.1')
    

    2.使用闭包暂停作用域

    然后我们来写一下generator的核心功能:暂停代码执行。

    1是起始状态,2是request成功后的状态,3是request失败的状态。我们再闭包中定义一个state用于追踪状态:

    function foo(url){
        let state;
    }
    

    我们在foo中定义一个process函数来对不同的state进行处理~

    function foo(url){
        let state,val;
        function process(v){
            switch(state){
                case 1:
                console.log('requesting',url)
                return request(url);
                case 2:
                val=v
                console.log(val)
                return;
                case 3:
                const err=v
                console.error(err)
                return;
            }
        }
    
    }
    

    每次需要处理一个新状态就会调用process。

    3.定义迭代器函数的代码

        return {
            next(v){
                if(!state){
                    state=1
                    return {
                        done:false,
                        value:process()
                    }
                }else if(state==1){
                    state=2
                    return {
                        done:true,
                        value:process(v)
                    }
                }else {
                    return {
                        done:true,
                        value:undefined
                    }
                }
            },
            throw(e){
                if(state==1){
                    state=3
                    return {
                        done:true,
                        value:process(e)
                    }
                }else{
                    throw e
                }
            }
        }
    

    调用:

    const it=foo('this is url')
    it.next()
    // requesting this is url
    // {done: false, value: Promise}
    it.next()
    // {done: true, value: undefined}
    

    相关文章

      网友评论

        本文标题:详解generator(三)——处理thunk

        本文链接:https://www.haomeiwen.com/subject/hnzgrftx.html