一.问题描述
协程的本质其实就是:以任务为单位对线程进行切片。下面是一个具体项目中协程的应用示例,请看下面的应用场景图:
以上应用场景为微信中的消息转发场景。在Message模块选择一条消息后,去到Contact模块选择联系人后,弹出确认弹窗,等待用户输入一些其他文本内容后确认即可,构造对应的转发消息通过IM通道发送给选择了的联系人对象。以下为项目初期的实现代码:
transferPicker(merge: boolean, transferData: IMChatMessage[]) {
const pickerParams: PickerContactBaseParam = {
finishedPicker: (pickedModels: PickedBaseModel[], _?: string | object | null) => {
MLog.D(TAG, 'transferPicker', pickedModels)
this.dialogController = new CustomDialogController({
builder: ShareContentDialog({
pickedModels: pickedModels,
contentData: { content_text: contentText },
onConfirm: async (contentData: ShareContentData, shareText?: string) => {
MLog.D(TAG, 'transferPicker onConfirm', pickedModels)
for (let i = 0; i < pickedModels.length; i++) {
const target = pickedModels[i]
let session: ISession | undefined = undefined
if (target instanceof PickedUserModel) {
session = await MsgSendUtils.getOrCreateSession(target.userId, target.appKey, false)
} else if (target instanceof PickedGroupModel) {
session = await MsgSendUtils.getOrCreateSession(target.groupId, target.appKey, true)
}
if (session) {
const selectMsgs = MsgUtils.toChatMsgsId(transferData)
if (merge) {
msgSend.sendMergeMsg(session.sid, selectMsgs)
} else {
for (let i = 0; i < selectMsgs.length; i++) {
msgSend.sendForwardMsg(session.sid, selectMsgs[i])
}
}
if (shareText) {
msgSend.sendMsg(session.sid, shareText)
}
}
}
msgSelectUtils.clean()
this.checkBoxVisible = false
MNavRouteHelper.popToName('ConversationDetailPage')
}
}),
alignment: DialogAlignment.Center,
width: '85%',
customStyle: true,
})
this.dialogController.open()
},
entranceType: EnterContactActionType.IM_MESSAGE_TYPE,
}
MNavRouteHelper.push(ContactPickerInterFace.HomeRouter, pickerParams)
}
代码分析:
1.以匿名函数的方式接收从Contact模块选择的联系人/群后;
2.将选择了的人/群作为参数弹出确认弹窗
3.在弹窗的匿名函数中接收用户确认操作后,遍历选择了的联系人数组后,通过IM通道将消息转发出去
存在的问题:
1.选择人/群逻辑可以封装
2.弹窗实现细节也可以封装
3.最大的问题还是匿名函数嵌套匿名函数,加深代码复杂度
二.解决方案
采用协程的方案替换以上的匿名函数嵌套问题:
public async transferMsg(msgText: string) {
//选人
const pickedModels = await ContactPickerInterFace.pickContactFromPickHome(EnterContactActionType.IM_MESSAGE_TYPE)
//确认弹窗
const shareText = await ShareDiagPromptUtil.showDialog(pickedModels, {
content_text: msgText,
})
//转发消息
ShareImMsgUtil.shareTextMsg(pickedModels, msgText)
if (shareText) {
ShareImMsgUtil.shareTextMsg(pickedModels, shareText)
}
//退出选择器
MNavRouteHelper.popToPreviousByName(ContactPickerInterFace.HomeRouter)
}
以上代码采用协程的方案将:1.选人;2.确认弹窗;3.转发IM消息。三个步骤封装解耦合的非常简洁,清晰,明了。
选人任务以协程形式封装示例:
static async pickContactFromPickHome(entranceType:EnterContactActionType):Promise<PickedBaseModel[]> {
return new Promise((resolve: Function, reject: Function) => {
const pickerParams: PickerContactBaseParam = {
finishedPicker: (pickedModels: PickedBaseModel[], extra?: object | string | null) => {
//选择结束
resolve(pickedModels)
},
entranceType: entranceType,
}
MNavRouteHelper.push(ContactPickerInterFace.HomeRouter, pickerParams)
})
}
弹窗任务以协程形式封装示例:
async showDialog(pickedModels: PickedBaseModel[], contentData: ShareContentData): Promise<string | undefined> {
const uiContext: UIContext = WindowUtil.windowStage.getMainWindowSync().getUIContext()
this.promptAction = uiContext.getPromptAction();
return new Promise(async (resolve, reject) => {
const options: ShareDialogPromptParam = {
pickedModels: pickedModels,
contentData: contentData,
onConfirm: (shareText?: string) => {
this.closeCustomDialog()
resolve(shareText)
},
cancelCallBack: () => {
this.closeCustomDialog()
}
}
this.contentNode = new ComponentContent(uiContext, wrapBuilder(customDialogBuilder), options);
//创建弹框
if (this.promptAction) {
this.promptAction.openCustomDialog(
this.contentNode,
{
showInSubWindow: false,
offset: { dx: 0, dy: 0 },
alignment: DialogAlignment.Center,
maskRect: {
x: 0,
y: 0,
width: '100%',
height: '100%'
},
isModal: true
})
}
})
}