挂载生命周期挂钩中的异步等待

async await in mounted lifecycle hook

我希望我的组件等到 promise 在挂载的生命周期挂钩中得到解决,但它一直显示“未定义”(因为呈现组件时数据不存在)。

<template>
    <div class="min-w-[800px] w-full h-screen overflow-hidden">
        <div class="flex flex-col">
            <TheHeader />
            <div class="flex mt-4 h-[830px] overflow-hidden">
                <section id="incoming-chats" class="min-w-[344px] px-2">
                    <BaseButton buttonType="btnPrimary" class="w-full mb-4"
                        >Serve Request</BaseButton
                    >
                    <TheChatQueue class="max-h-screen overflow-y-scroll pb-4" />
                </section>
                <section id="content" class="w-full mx-2 pr-2" v-if="dataReady">
                    <BaseTabsWrapper>
                        <BaseTab
                            v-for="chatSession in incomingChatSessions"
                            :key="chatSession.id"
                            :title="chatSession.endUser.name"
                        >
                            <p>{{ chatSession }}</p>
                        </BaseTab>
                    </BaseTabsWrapper>
                </section>
                <!-- <section id="content" class="w-full mx-2 pr-2">
                    Please take a chat
                </section> -->
            </div>
        </div>
    </div>
</template>

<script>
import { mapActions, mapGetters } from 'vuex'
import BaseButton from '@/components/BaseButton.vue'
import BaseClientHeader from '../components/BaseClientHeader.vue'
import BaseTabsWrapper from '@/components/BaseTabsWrapper.vue'
import BaseTab from '@/components/BaseTab.vue'
import BaseTicketEditor from '@/components/BaseTicketEditor.vue'
import BaseTicketHistorical from '@/components/BaseTicketHistorical.vue'
import TheChatQueue from '@/components/TheChatQueue.vue'
import TheHeader from '@/components/TheHeader.vue'
import TheChatArea from '@/components/TheChatArea.vue'
export default {
    components: {
        BaseButton,
        // BaseClientHeader,
        BaseTabsWrapper,
        BaseTab,
        // BaseTicketEditor,
        // BaseTicketHistorical,
        // TheChatArea,
        TheChatQueue,
        TheHeader,
    },
    data() {
        return {
            dataReady: false,
        }
    },
    async mounted() {
        try {
            await this.callWebsocket()
            this.dataReady = true
        } catch {
            console.log('error')
        }
    },
    computed: {
        ...mapGetters({
            incomingChatSessions: 'chatSession/getIncomingChatSessions',
        }),
    },
    methods: {
        ...mapActions({
            callWebsocket: 'websocket/processWebsocket',
        }),
    },
}
</script>

如您所见,为了创建动态选项卡菜单,我在来自处理 websocket 的 vuex 模块的计算值中循环。这里的主要问题是,即使我在主组件 (dataReady) 中有一个标志,该组件在渲染时也是空的。

这是包含 processwebsocket 操作的 websocket 模块。所有这一切的重要部分都在代码的“case 'chat-updated':”部分内:

export const namespaced = true
export const state = {
    connected: false,
    error: null,
    connectionId: '',
    incomingChats: [],
    socket: {},
}
export const actions = {
    async processWebsocket({ dispatch, rootState, commit }) {
        const socket = await new WebSocket('wss://xyz')
        socket.onopen = function (event) {
            console.log('Websocket connected.')
            commit('SET_CONNECTION', event.type)
            dispatch(
                'sendMessage',
                JSON.stringify({ message: 'Hello, server.' })
            )
        }
        socket.onmessage = function (event) {
            const socketData = JSON.parse(event.data)
            const socketDataType = socketData.type
            if (
                socketData.connectionId &&
                socketData.connectionId !== state.connectionId
            ) {
                commit('SET_CONNECTION_ID', socketData.connectionId)
                dispatch(
                    'shifts/updateEventsSubscription',
                    rootState.token.agentId,
                    {
                        root: true,
                    }
                )
            } else {
                switch (socketDataType) {
                    case 'incoming-chats-updated':
                        commit('SET_INCOMING_CHATS', socketData.incomingChats)
                        break
                    case 'chat-updated':
                        console.log(socketData)
                        const chatSession = socketData.chat
                        const chatEvents = chatSession.events
                        const chatState = chatEvents.length - 1
                        if (
                            chatEvents[chatState].type === 'ChatAgentAssigned'
                        ) {
                            dispatch(
                                'chatSession/fetchChatSession',
                                chatSession,
                                {
                                    root: true,
                                }
                            )
                        } else {
                            console.log(
                                `Other Event ${chatEvents[chatState].type}`
                            )
                        }
                        break
                }
            }
        }
        socket.onerror = function (event) {
            console.log('webSocket: on error: ', event)
        }
        socket.onclose = function (event) {
            console.log('webSocket: on close: ', event)
            commit('SET_CONNECTION')
            state.socket = null
            setTimeout(startWebsocket, 5000)
        }
        commit('SET_SOCKET', socket)
    },
    async waitForOpenConnection() {
        return new Promise((resolve, reject) => {
            const maxNumberOfAttempts = 10
            const intervalTime = 200
            let currentAttempt = 0
            const interval = setInterval(() => {
                if (currentAttempt > maxNumberOfAttempts - 1) {
                    clearInterval(interval)
                    reject(new Error('Maximum number of attempts exceeded.'))
                } else if (state.socket.readyState === state.socket.OPEN) {
                    clearInterval(interval)
                    resolve()
                }
                currentAttempt++
            }, intervalTime)
        })
    },
    async sendMessage({ dispatch }, message) {
        if (state.socket.readyState !== state.socket.OPEN) {
            try {
                await dispatch('waitForOpenConnection', state.socket)
                state.socket.send(message)
            } catch (err) {
                console.error(err)
            }
        } else {
            state.socket.send(message)
        }
    },
}
export const mutations = {
    SET_CONNECTION(state, message) {
        if (message == 'open') {
            state.connected = true
        } else state.connected = false
    },
    SET_CONNECTION_ID(state, connectionId) {
        state.connectionId = connectionId
    },
    SET_SOCKET(state, socket) {
        state.socket = socket
    },
    SET_INCOMING_CHATS(state, incomingChats) {
        state.incomingChats = incomingChats
    },
    SET_ERROR(state, error) {
        state.error = error
    },
}
export const getters = {
    getIncomingChats: (state) => {
        return state.incomingChats
    },
    getConnectionId: (state) => {
        return state.connectionId
    },
}

这是我用来填充选项卡菜单的 chatSession 模块:

export const namespaced = true
export const state = () => ({
    chatSessions: [],
})
export const actions = {
    addChatSession({ commit }, chatSession) {
        commit('ADD_CHAT_SESSION', chatSession)
    },
    fetchChatSession({ commit }, chatSession) {
        commit('SET_CHAT_SESSION', chatSession)
    },
    clearChatSessions({ commit }) {
        commit('CLEAR_CHAT_SESSIONS')
    },
}
export const mutations = {
    ADD_CHAT_SESSION(state, chatSession) {
        state.chatSessions.push({
            ...chatSession,
        })
    },
    SET_CHAT_SESSION(state, chatSession) {
        state.chatSessions.push({
            ...chatSession,
        })
    },
    CLEAR_CHAT_SESSIONS(state) {
        state.chatSessions = []
    },
}
export const getters = {
    getIncomingChatSessions: (state) => {
        return state.chatSessions
    },
}

查看 this: 将 this.dataReady = true 替换为 this.$set(this, 'dataReady', true)

正如我所说,processWebsocket 将在建立与 websocket 的连接时解决操作 const socket = await new WebSocket('wss://xyz') 此时 getter getIncomingChatSessions 将 return空数组(默认值)。 getIncomingChatSessions 中的数据仅在 socket.onmessage 事件触发时可用。

async mounted() {      
 await this.callWebsocket()
 this.dataReady = true
 // this.incomingChatSessions is yet empty here but this.dataReady is true, so UI is rendering but list is empty.   
},