美文网首页
vuex的dispatch是同步还是异步?

vuex的dispatch是同步还是异步?

作者: 科科Cole | 来源:发表于2021-09-01 16:34 被阅读0次

我们知道在vuex中,通过提交commit,触发mutations中的方法,来改变state中的状态。Mutation必须是同步函数,异步方法需要写在Action中,在Action中提交commit来改变state的状态,这点在Mutation | Vuex (vuejs.org)中都有说明。
Mutation是同步函数,这点没什么疑问。比如有一个store:

export default new Vuex.Store({
    state:{
        someWords: 'default',
    },
    mutations:{
        UPDATE_SOME_WORDS(state, someWords){
            state.someWords = someWords;
        },
    },
    actions:{
        updateSomeWords({commit}, someWords){
            commit('UPDATE_SOME_WORDS', someWords);
        },
    },
})

对于下面一段代码:

console.log(store.state.someWords);
store.commit('UPDATE_SOME_WORDS', 'updated');
console.log(store.state.someWords);

应该输出default updated,因为mutation是同步的,提交commit后state立马会改变。
我们看到官网文档中说,Action通常是异步的,并且Action可以返回Promise,因此可以这么写:store.dispatch('actionA').then(()=>{//...})。那么action中的方法,本身是同步执行还是异步执行的呢?对于下面一段代码:

console.log(store.state.someWords);
store.dispatch('updateSomeWords', 'updated');
console.log(store.state.someWords);

输出为什么是default updated呢?Action不应该是异步的吗?
了解这个问题之前,首先需要看一下关于Promise的一些问题:ES6 Promise。之后来看看vuex源码store.js - vuejs/vuex
Store构造函数中会初始化一个私有属性_actions:

this._actions = Object.create(null) // 创建一个没有继承原型方法的空对象

之后会执行installModule方法:

function installModule (store, rootState, path, module, hot) {
  // 安装根Module
  // ...
  module.forEachAction((action, key) => { // 遍历Module中的action,对每个action执行该匿名函数,参数为:action函数,函数名
    const type = action.root ? key : namespace + key // 根Module ? 函数名 : 命名空间 + 函数名
    const handler = action.handler || action // 获取相应action函数
    registerAction(store, type, handler, local) // 注册action
  })
  // 安装Store中每个Module
  // ...
}

registerAction方法中会注册每个action:

function registerAction (store, type, handler, local) { // 参数:store实例,action名,action函数,上下文
  const entry = store._actions[type] || (store._actions[type] = []) // _actions中没[type]属性,则初始化该属性为空数组
  entry.push(function wrappedActionHandler (payload) { // 往_actions中push该函数
    let res = handler.call(store, {
      dispatch: local.dispatch,
      commit: local.commit,
      getters: local.getters,
      state: local.state,
      rootGetters: store.getters,
      rootState: store.state
    }, payload) // 执行该action函数,上下文为store,参数为:一个与store实例有相同方法和属性的对象,payload
    if (!isPromise(res)) { // 如果res不是Promise对象,则包装成fulfilled的Promise对象
      res = Promise.resolve(res)
    }
    if (store._devtoolHook) {
      return res.catch(err => {
        store._devtoolHook.emit('vuex:error', err)
        throw err
      })
    } else {
      return res // 返回action执行结果,为一个Promise
    }
  })
}

当我们调用store.dispatch时,触发dispatch方法:

dispatch (_type, _payload) {
    // check object-style dispatch
    const {
      type,
      payload
    } = unifyObjectStyle(_type, _payload) // dispatch参数可以是(type, payload),也可以是({type, payload}),这里规整成一个统一的对象

    const action = { type, payload } // type为action名,payload为参数
    const entry = this._actions[type] // 这里的entry是一个函数,即registerAction中push的函数
    if (!entry) {
      if (__DEV__) {
        console.error(`[vuex] unknown action type: ${type}`)
      }
      return
    }

    try {
      this._actionSubscribers
        .slice() // shallow copy to prevent iterator invalidation if subscriber synchronously calls unsubscribe
        .filter(sub => sub.before)
        .forEach(sub => sub.before(action, this.state))
    } catch (e) {
      if (__DEV__) {
        console.warn(`[vuex] error in before action subscribers: `)
        console.error(e)
      }
    }

    const result = entry.length > 1
      ? Promise.all(entry.map(handler => handler(payload)))
      : entry[0](payload) // 重点在于这一步。如果有多个同名action函数,则构造成Promise.all对象,否则直接去执行entry中的函数,也即action函数。我们知道,Promise构造函数参数函数是会立即执行的,所以对于action中全是同步方法的action,会立即执行,不会去生成异步方法。

    return new Promise((resolve, reject) => { // 返回Promise,当result的Promise resolve()或reject()后,该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)
      })
    })
  }

因此,当action中只有同步方法时,dispatch操作实际上也是同步的,当action中有异步方法时,异步方法的then或catch会异步执行。

相关文章

网友评论

      本文标题:vuex的dispatch是同步还是异步?

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