Vuex:在调度操作之前等待 websocket 响应

Vuex: Wait for websocket response before dispatching action

这是场景/前提:

问题是由于 websocket 的异步行为(或者这就是我的想法,如果我错了请纠正我)我在尝试将 put to 时总是得到一个空的“connectionId”那个“订阅”API。我已经尝试过 async/await 和承诺,但似乎没有任何效果。我对 async/await 和 Vuex 的 websockets 很陌生,所以很确定我做错了什么。

这是用户 vuex 模块,我在其中执行所有 login/token 操作并从 shift vuex 模块调度“updateEventsSubscription”操作。为了使“updateEventsSubscription”操作起作用,我需要从“processWebsocket”操作(获取 connectionId 参数)和来自 shifts vuex 模块的“startShift”操作(获取 shiftId 参数)获取响应:

import UserService from '@/services/UserService.js'
import TokenService from '@/services/TokenService.js'
import router from '@/router'
export const namespaced = true
export const state = {
    accessToken: '',
    errorMessage: '',
    errorState: false,
    userEmail: localStorage.getItem('userEmail'),
    userPassword: localStorage.getItem('userPassword'),
}
export const mutations = {
    SET_TOKEN(state, accessToken) {
        state.accessToken = accessToken
        TokenService.saveToken(accessToken)
    },
    SET_USER(state, authUserJson) {
        state.userEmail = authUserJson.email
        state.userPassword = authUserJson.password
        localStorage.setItem('userPassword', authUserJson.password)
        localStorage.setItem('userEmail', authUserJson.email)
    },
    SET_ERROR(state, error) {
        state.errorState = true
        state.errorMessage = error.data.error_description
    },
    CLOSE_NOTIFICATION(state, newErrorState) {
        state.errorState = newErrorState
    },
}
export const actions = {
    signIn({ commit, dispatch, rootState }, authUserJson) {
        return UserService.authUser(authUserJson)
            .then((result) => {
                commit('SET_USER', authUserJson)
                commit('SET_TOKEN', result.data.access_token)
                dispatch('token/decodeToken', result.data.access_token, {
                    root: true,
                })
                dispatch(
                    'shifts/updateEventsSubscription',
                    rootState.token.agentId,
                    {
                        root: true,
                    }
                )
                router.push('/support')
            })
            .catch((error) => {
                console.log(error)
                if (error.response.status === 400) {
                    commit('SET_TOKEN', null)
                    commit('SET_USER', {})
                    commit('SET_ERROR', error.response)
                } else {
                    console.log(error.response)
                }
            })
    },
    signOut({ commit }) {
        commit('SET_TOKEN', null)
        commit('SET_USER', {})
        localStorage.removeItem('userPassword')
        localStorage.removeItem('userEmail')
        TokenService.removeToken()
        router.push('/')
    },
    closeNotification({ commit }, newErrorState) {
        commit('CLOSE_NOTIFICATION', newErrorState)
    },
}
export const getters = {
    getToken: (state) => {
        return state.accessToken
    },
    errorState: (state) => {
        return state.errorState
    },
    errorMessage: (state) => {
        return state.errorMessage
    },
    isAuthenticated: (state) => {
        return state.accessToken
    },
    userEmail: (state) => {
        return state.userEmail
    },
    userPassword: (state) => {
        return state.userPassword
    },
}

这是 websocket 存储:我将 connectionId 传递给状态,以便能够在另一个 vuex 操作中使用它来订阅新聊天:

export const namespaced = true
export const state = {
    connected: false,
    error: null,
    connectionId: '',
    statusCode: '',
    incomingChatInfo: [],
    remoteMessage: [],
    messageType: '',
    ws: null,
}
export const actions = {
    processWebsocket({ commit }) {
        const v = this
        this.ws = new WebSocket('mywebsocket')
        this.ws.onopen = function (event) {
            commit('SET_CONNECTION', event.type)
            v.ws.send('message')
        }
        this.ws.onmessage = function (event) {
            commit('SET_REMOTE_DATA', event)
        }
        this.ws.onerror = function (event) {
            console.log('webSocket: on error: ', event)
        }
        this.ws.onclose = function (event) {
            console.log('webSocket: on close: ', event)
            commit('SET_CONNECTION')
            ws = null
            setTimeout(startWebsocket, 5000)
        }
    },
}
export const mutations = {
    SET_REMOTE_DATA(state, remoteData) {
        const wsData = JSON.parse(remoteData.data)
        if (wsData.connectionId) {
            state.connectionId = wsData.connectionId
            console.log(`Retrieving Connection ID ${state.connectionId}`)
        } else {
            console.log(`We got chats !!`)
            state.messageType = wsData.type
            state.incomingChatInfo = wsData.documents
        }
    },
    SET_CONNECTION(state, message) {
        if (message == 'open') {
            state.connected = true
        } else state.connected = false
    },
    SET_ERROR(state, error) {
        state.error = error
    },
}

最后这是轮班商店(问题所在),如您所见,我有一个 startShift 操作(一切正常),然后是“updateEventsSubscription”,我正在尝试等待来自“startShift”操作和“processWebsocket”操作的响应。调试应用程序我意识到 startShift 操作一切正常,但 websocket 操作在“updateEventsSubscription”需要它后发送响应,当我尝试对该 API 进行放置时导致错误(因为它需要 connectionId来自 websocket 状态的参数)。

import ShiftService from '@/services/ShiftService.js'
export const namespaced = true
export const state = {
    connectionId: '',
    shiftId: '',
    agentShiftInfo: '{}',
}
export const actions = {
    startShift({ commit }, agentId) {
        return ShiftService.startShift(agentId)
            .then((response) => {
                if (response.status === 200) {
                    commit('START_SHIFT', response.data.aggregateId)
                }
            })
            .catch((error) => {
                console.log(error)
                if (error.response.status === 401) {
                    console.log('Error in Response')
                }
            })
    },
    async updateEventsSubscription({ dispatch, commit, rootState }, agentId) {
        await dispatch('startShift', agentId)
        const shiftId = state.shiftId
        await dispatch('websocket/processWebsocket', null, { root: true })
        let agentShiftInfo = {
            aggregateId: state.shiftId,
            connectionId: rootState.websocket.connectionId,
        }
        console.log(agentShiftInfo)
        return ShiftService.updateEventsSubscription(shiftId, agentShiftInfo)
            .then((response) => {
                commit('UPDATE_EVENTS_SUBSCRIPTION', response.data)
            })
            .catch((error) => {
                if (error.response.status === 401) {
                    console.log('Error in Response')
                }
            })
    },
}
export const mutations = {
    START_SHIFT(state, shiftId) {
        state.shiftId = shiftId
        console.log(`Retrieving Shift ID: ${state.shiftId}`)
    },
    UPDATE_EVENTS_SUBSCRIPTION(state, agentShiftInfo) {
        state.agentShiftInfo = agentShiftInfo
    },
}

您应该将 WebSocket 操作转换为在连接 WebSocket 时解析的承诺。:

export const actions = {
    processWebsocket({ commit }) {
      return new Promise(resolve=> {
        const v = this
        this.ws = new WebSocket('mywebsocket')
        this.ws.onopen = function (event) {
            commit('SET_CONNECTION', event.type)
            v.ws.send('message')
              resolve();
        }
        this.ws.onmessage = function (event) {
            commit('SET_REMOTE_DATA', event)
        }
        this.ws.onerror = function (event) {
            console.log('webSocket: on error: ', event)
        }
        this.ws.onclose = function (event) {
            console.log('webSocket: on close: ', event)
            commit('SET_CONNECTION')
            ws = null
            setTimeout(startWebsocket, 5000)
        }
     });
    },
}

所以我意识到我必须改为解决 this.ws.message 上的承诺。通过这样做我的所有数据都相应地填充,仍然存在同步问题(我现在无法提供 websocket 状态,因为由于其异步行为,当其他组件尝试通过以下方式使用它时,状态还不存在:rootGetters.websocket.incomingChats 例如)但我想这是另一个问题的一部分。这是模块操作的最终版本:

export const actions = {
    processWebsocket({ commit }) {
        return new Promise((resolve) => {
            const v = this
            this.ws = new WebSocket('wss://ws.rubiko.io')
            this.ws.onopen = function (event) {
                commit('SET_CONNECTION', event.type)
                v.ws.send('message')
            }
            this.ws.onmessage = function (event) {
                commit('SET_REMOTE_DATA', event)
                resolve(event)
            }
            this.ws.onerror = function (event) {
                console.log('webSocket: on error: ', event)
            }
            this.ws.onclose = function (event) {
                console.log('webSocket: on close: ', event)
                commit('SET_CONNECTION')
                ws = null
                setTimeout(startWebsocket, 5000)
            }
        })
    },
}

无论如何,谢谢@Eldar,你走对了路。