Oasis's Cloud

一个人的首要责任,就是要有雄心。雄心是一种高尚的激情,它可以采取多种合理的形式。
—— 《一个数学家的辩白》

异步编程


消息队列

异步编程中的回调函数是一个很重要的概念,然而,通过回调函数会毁掉我们的代码,导致回调地狱的出现。 JavaScript 的开发人员向该语言添加了 promise 和 async await。

JavaScript 中的异步实现是基于事件循环的。JavaScript 的事件循环模型中有一个消息队列,JavaScript 不断的从消息队列中取出等待的消息,进行消费。

有些方法可以改变消息队列,比如 setTimeout。setTimeout 的第二个参数可以设置为 0,设置为 0 并不意味着马上执行,而是要等到消息队列前面的消息处理完,才能轮到有 setTimeout 放入的消息。所以,setTimeout 的计时器并不准确。大部分网页中的倒计时程序都是由 setTimeout 进行实现的,在秒杀倒计时的场景中,秒杀开始时间为 8点,当到8点的时候,有可能我们还不能点击秒杀按钮。

注意点:web worker、跨域 iframe 有自己独立的栈、堆和消息队列。相当于两个不同的运行时。如果他们要通信,需要通过 postMessage。

这里需要思考一个问题,何时从刚消息队列中取数据消费呢?

setTimeout 和 Promise 的区别

通过 debugger 进行调试。设计用例。

可参考链接:

  1. https://www.youtube.com/watch?v=8aGhZQkoFbQ
  2. https://www.youtube.com/watch?v=cCOL7MC4Pl0

设计自己的 Promise

如何设计 Promise,我们既然要做 Promise 的 demo,那我们最终产物的使用方式要和 Promise 是一致的。所以我们要先思考清楚我们产物的用法。(ps: 这体现了从上向下的设计思路。先设计大的框架,定义好使用方法,再想办法实现。)

// 我们的文件
import OasisPromise from './oasis-promise.js'
// 使用方式
new OasisPromise ((reslove, reject) => {
    console.log('registry')
    setTimeout(() => {
        console.log('reslove callback')
        reslove('resloved')
    }, 0)
}).then((value) => {
    console.log('then value', value)
    return value
})

基于上述设计中的使用方式,我们首先要考虑链式调用如何实现,再 JavaScript 中,链式调用通过对象的方式可以实现。如下例:

// oasis-promise.js

class OasisPromise {
    constructor() {}
    then() {
        return this
    }
}

然后我们那可以写:

new OasisPromise().then()

继续扩充我们的 OasisPromise 类。由其用法我们知道,在构造函数中需要传一个方式,此方法包含两个内置的函数(reslove,reject)作为参数。我们先来实现这一步:

// oasis-promise.js

class OasisPromise {
    constructor(action) {
        if(action) {
            action(this.reslove.bind(this), this.reject.bind(this))
        }
    }
    reslove(value) {
       console.log(value) 
    }
    reject(error) {
       console.error(error) 
    }
    then(action) {
        console.log('then')
        return this
    }
}

new OasisPromise((reslove,reject) => {
    reslove('outter')
}).then()
new Promise().then()

reslove 要做的事情是用此阶段的 value,传给下一个 then 中的 handler,所以,在 then 方法调用的时候要能存下 then 方法的 handler,这里用了 handlers 数组。

// oasis-promise.js

class OasisPromise {
    constructor(action) {
        this.errorHanlders = () => {}
        this.hanlders = []
        if(action) {
            action(this.resloveEvent.bind(this), this.rejectEvent.bind(this))
        }
    }
    resloveEvent(value) {
        let nextValue = value
        try {
            this.hanlders.forEach(handler => {
                nextValue = handler(nextValue)
            })  
        } catch (e) {
            this.hanlders = []
            this.rejectEvent(e)
        }

    }
    rejectEvent(error) {
        this.errorHandlers(error)
    }
    then(thenHandler) {
        this.hanlders.push(thenHandler)
        return this
    }
    catch(err) {
        this.errorHandlers = err
        return this
    }
}

在上面这段代码的测试 demo 如下:

// Hi Oasis
new OasisPromise((reslove, reject) => {
        setTimeout(() => {
            reslove('Hi Oasis')
        } ,0)
    }).then((value)=> console.log(value))

// 无输出,因为 reslove 不是异步,导致then方法还未执行,所以 handlers 为空数组
new OasisPromise((reslove, reject) => {
    // setTimeout(() => {
        reslove('Hi Oasis')
    // } ,0)
}).then((value)=> console.log(value))

// reslove 在这里是异步的
new Promise((reslove, reject) => reslove('Hi Oasis'))
    .then((value) => console.log(value))

可以看出,promise 的方式还是要写很多模板代码,这时我们可以通过 async await 来完成同样的效果。 async await 在 nodejs 中会编译为 promise 的形式,所以 async 和 await 要配对,否则在翻译的时候找不到 then 的上下文。

为了看到效果,可访问如下链接: babel online

附加内容:下面代码的 AST 是什么样子的?可以通过 https://astexplorer.net/ 来观察