scheduler

by jin5354

HTML

<script src="https://cdn.jsdelivr.net/lodash/4.17.4/lodash.min.js"></script>

Babel + JSX

// more detail go to 
// https://github.com/jin5354/leaf-observable

let depUid = 1
let watcherUid = 1

const supportMO = typeof MutationObserver !== 'undefined'

function nextTick(task) {
  return (() => {
    /* istanbul ignore if */
    if(supportMO) {
      let counter = 1
      const observer = new MutationObserver(task)
      const textNode = document.createTextNode(String(counter))
      observer.observe(textNode, {characterData: true})
      textNode.textContent = 2
    }else {
      return Promise.resolve().then(task)
    }
  })()
}

const queue = []
const watcherIds = {}
let waiting = false

function flushSchedulerQueue() {
  queue.forEach(watcher => {
    watcher.run()
  })
  queue.length = 0
  watcherIds = {}
  waiting = false
}

function queueWatcher(watcher) {
  const uid = watcher.uid

  if(!watcherIds[uid]) {
    watcherIds[uid] = true
    queue.push(watcher)

    if (!waiting) {
      waiting = true
      nextTick(flushSchedulerQueue)
    }
  }
}

class Dep {

  constructor() {
    this.id = depUid++ // 为每个 dep 标记一个 uid
    this.subs = []
  }

  addSub(sub) {
    this.subs.push(sub)
  }

  depend() { // 依赖收集函数,在 getter 中执行,在 Dep.target 上找到当前 watcher,并添加依赖
    Dep.target && Dep.target.addDep(this)
  }

  notify() {
    this.subs.forEach(sub => {
      sub.update()
    })
  }

}
Dep.target = null // Dep.target 用来暂存正在收集依赖的当前 watcher

// 现在 Wactcher 接收三个参数,第一个为依赖收集函数(如上文的 render),第二个为回调,第三个为附加配置
class Watcher {

  constructor(expFn, cb, options = {}) {
  	this.uid = watcherUid++
    this.context = options.context
    this.immediate = options.immediate
    this.expFn = expFn
    this.depIds = new Set()  //标记当前 watcher 已经加入到了哪些 dep
    this.cb = cb
    this.value = this.subAndGetValue()
    this.clonedOldValue = _.cloneDeep(this.value)
  }

	/**
   * [update 更新,根据 immediate 参数判断是否立刻执行回调]
   */
  update() {
    if(this.immediate) {
      this.run()
    }else {
      queueWatcher(this)
    }
  }
  
  /**
   * [run 执行回调]
   */
  run() {
    let value =...