Skip to content

dispatch

javascript
class Store {
  constructor () {
    // store.prototype.dispatch 接收 type 和 payload 两个个参数
    // 根据 type 去 store._actions 中找出对应的数组,这个数组内存储的都是这个 type
    // 的 action,遍历数组并执行 wrappedActionHandler 同时传入 payload。
    // 直接这样做有个问题。
    // context.commit 或 context.dispatch 会调用
    // store.prototype.commit 或 store.prototype.dispatch,此时 this 是 context
    // context._mutations 或 context._actions 值为 undefined。
    // this._actions[type] 或 this._mutations[type] 就会报错
    // 或者我们对 action 的 context 解构出 dispatch 和 commit
    // 再调用 dispatch 或 commit 时,会调用
    // store.prototype.commit 或 tore.prototype.dispatch,此时 this 是 undefined
    // this._actions[type] 或 this._mutations[type] 就会报错
    // 因此不能让实例直接调用父类的 dispatch 和 commit 方法
    // 而是需要调用前需要改变父类的 dispatch 和 commit 方法的 this,因此需要劫持。
    // 在实例自身中也定义 dispatch 和 commit 方法,这样最先执行的是实例自身的方法
    // 实例自身的方法中再执行原型中的 dispatch 和 commit 并修改this 为 store
    // 同时传入 type 和 payload,但是实例的方法已经重写原型的,因此在定义实例
    // 方法前先取出原型中的方法并保存即可。
    // 以上操作必须在 installModule 之前完成,因为 installModule 中生成 context,
    // context.commit = store.commit,如果在之后完成,context.commit 总是执行
    // Store.prototype.commit,不会执行我们定义在 store 上的,就没机会修改 this 了。
    // bind commit and dispatch to self
    const store = this
    const { dispatch, commit } = this
    this.dispatch = function boundDispatch (type, payload) {
      return dispatch.call(store, type, payload)
    }
    this.commit = function boundCommit (type, payload, options) {
      return commit.call(store, type, payload, options)
    }
  }

  commit (type, payload) {
    const entry = this._mutations[type]
    if (!entry) {
      return
    }
    entry.forEach(function commitIterator (handler) {
      handler(payload)
    })
  }

  dispatch (type, payload) {
    const entry = this._actions[type]
    if (!entry) {
      return
    }
    const result = entry.length > 1
      ? Promise.all(entry.map(handler => handler(payload)))
      : entry[0](payload)
    return new Promise((resolve, reject) => {
      result.then(res => {
        try {
          this._actionSubscribers
            .filter(sub => sub.after)
            .forEach(sub => sub.after(action, this.state))
        } catch (e) {
          if (__DEV__) {
            console.warn(`[vuex] error in after action subscribers: `)
            console.error(e)
          }
        }
        resolve(res)
      }, error => {
        try {
          this._actionSubscribers
            .filter(sub => sub.error)
            .forEach(sub => sub.error(action, this.state, error))
        } catch (e) {
          if (__DEV__) {
            console.warn(`[vuex] error in error action subscribers: `)
            console.error(e)
          }
        }
        reject(error)
      })
    })
  }
}