美文网首页我爱编程程序员
JS Promise: understanding and im

JS Promise: understanding and im

作者: davidhuangdw | 来源:发表于2018-03-26 18:18 被阅读24次

    The purpose: resolve the problem of ugly nested code

    Problem: ugly nested code

    1. Async requires using callbacks instead of return value:
      • For async function(e.g. make a api request) A(), we cannot get return value from A() immediately, but instead we have to pass a callback function cb(ret) to A, like A(..., cb, ...), to let A to call cb(ret) when A has done his async job
    2. Sequential async functions bring in ugly nested code:
      • if A, B, C are async functions, and we want the sequential execution order A -> B -> C -> D, then we have to pass B to A, C to B, D to C, which will be A(...,()=>B(...,()=>C(...,D..)..)..), which are uglily nested especially when we are wrting B,C,D as anonymous functions

    No one can change above condition 1(callback rule) because using callback is the built-in way how JS achieve non-blocking IO with single thread VM(based on event-loop).

    However, some smart guy invent the Promise object to solve condition 2 without changing condition 1: by remove callback parameters from function.

    Take above async A->B->C->D example:

    • Without promise object:
        A(...,()=>B(...,()=>C(...,D..)..)..)
    
    • With promise object:
        let A = (...) => new Promise((succ, fail) => A(...,succ,..fail,..)) // remove callback from parameters
        let B = (...) => new Promise((succ, fail) => B(...,succ,..fail,..))
        let C = (...) => new Promise((succ, fail) => C(...,succ,..fail,..))
    
        let a = A(..)
        a.then(B(..)).then(C(..)).then(D(..))
        a.then(x) // assume x,y are an existing Promise object
        a.then(y) 
    
        // the execution order:
        // A -> B -> C -> D
        //   -> X
        //   -> Y
    

    The idea of Promise

    • Use Promise to eliminate nested callbacks:
      1. by remove callback parameter from the original async function parameters
        • to remove it, we create an (promise)object to store both original async function and its callback parameter position
        • constructor will look like this:
          new Promise((succ, fail) => {...async_func(..,succ,...fail..)})
      2. implement #then(succ_callback, fail_callback) to insert callbacks later

    How to implement Promise

    The basic idea is:

    1. allows #then(on_fulfill, on_reject) to insert followup callback on_fulfill:
      • we call original async function immediately by passing in a mockup_callback
      • let mockup_callback() allows insert more followup_callback() logics afterward
        • we use a mutable_queue to store followup_callbacks:
          1. the queue initially empty on object creation
          2. #then(followup_callback) will store the followup_callback into the queue
          3. mockup_callback() will retrieve from queue and call all those followup_callback when the original async function actually calls mockup_callback(),
    2. allow chainable #then(): each #then(on_fulfill, on_reject) create a new promise object
      • means we store res => succ(on_fullfill(res)) instead of simply on_fulfill as a followup_callback
    3. allow pass in async logic into #then(), means that allowing on_fulfill() to return a Promise object instead of a return value
      • in this case, we store res => on_fulfill(res).then(succ) as a followup_callback

    In summary, a draft code will look like:

    class MyPromise{
        constructor(standard_async_func){ //standard_async_func has the same parameters format standard_async_func(followup_succ, followup_fail)
            this.queue = []
            let followup_succ = res => this.queue.forEach(followup => followup.succ(res))
            let followup_fail = exp => this.queue.forEach(followup => followup.fail(exp))
            standard_async_func(followup_succ, followup_fail)
        }
    
        then(onFulfill, onReject){
            return new MyPromise((followup_succ, followup_fail) => {
                // logic order:
                //   standard_async_func -> followup.succ -> onFulfill -> followup_succ
                //   standard_async_func -> followup.fail -> onReject -> followup_succ
                let followup = {
                    succ: res => {
                        let ret
                        try { ret = onFulfill(res)
                        }catch(e){ return followup_fail(e) } // immediately return when catch
    
                        ret.then ? ret.then(followup_succ, followup_fail) : followup_succ(ret)
                    },
                    fail: exp => {
                        if(!onReject) return followup_fail(exp)
    
                        let ret
                        try { ret = onReject(exp)
                        }catch(e){ return followup_fail(e) } // immediately return when catch
    
                        ret.then ? ret.then(followup_succ, followup_fail) : followup_succ(ret)
                    }
                }
                this.queue.push(followup)
            })
        }
    }
    
    //test
    let logging = callback => (r => {let ret = callback(r); console.log(ret); return ret})
    let inc = logging(val => (val+5))
    let dec = logging(val => (val-2))
    
    function createTimeoutPromise(val, seconds){
        return new MyPromise(succ => setTimeout((()=> succ(val)), 1000*seconds))
    }
    
    createTimeoutPromise(10, 2)
        .then(inc)
        .then(dec)
        .then(dec)
        .then(val => createTimeoutPromise(val, 3))
        .then(inc)
        .then(dec)
        .then(dec)
    
    

    相关文章

      网友评论

        本文标题:JS Promise: understanding and im

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