挂载生命周期挂钩中的异步等待
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.
},
我希望我的组件等到 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.
},