美文网首页我爱编程程序员
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