postMessage消息通信Promise化的方法实现

 更新时间:2024年03月13日 08:25:20   作者:ZTStory  
postMessage Api 想必大家都不陌生,WebWorker 通信会用到,iframe 窗口之间通信也会用到,那么我们能不能将 postMessage 进行一次转化,把他变成类似 Promise 的使用方式,所以本文给大家介绍了postMessage消息通信Promise化的方法实现,需要的朋友可以参考下

前言

postMessage Api 想必大家都不陌生,WebWorker 通信会用到,iframe 窗口之间通信也会用到,尤其像一些通过 iframe 嵌入其他项目产品的应用,想要实现实时通信,就少不了它。

但是,监听消息基本都是全局注册事件来接收对应消息,然后在做分发处理的。

假如业务比较复杂,流程比较长,消息通信的频率高,而且通信场景繁琐,那么通过全局事件处理就显得不太合适了。

那么问题来了,我们能不能将 postMessage 进行一次转化,把他变成类似 Promise 的使用方式,这样业务里使用 postMessage 进行通信的时候,就不需要考虑全局回调事件了。而且 Promise 和他的语法糖 await 都可以很好的消除回调地狱的情况。

思考

Promise 化,需要解决那些问题?

  • 业务中发起的事件,如何注册并消费?
  • 相同的事件多次发起,应该如何处理响应结果?

我们先来看业务中发起的事件,如何注册并消费?

举个实际的例子:我们假定有子系统向父级 iframe 获取 loginToken 的行为。

正常情况我们会这样写:

const messageEventHandler = (event: MessageEvent) => {
  const data = event.data;
  // 分发事件
  emit(data?.api, data);
}
// 接收
window.addEventListener("message", messageEventHandler);
// 发送
window.parent.postMessage({ api: "getLoginToken" }, "*");

这样写逻辑有问题吗?没有问题!逻辑可以正确的执行,事件也成功的被分发了

通过消息订阅来接收通知,单发的时候看起来没什么问题,假如 getLoginToken 被多次触发,或者说,同一个事件被多次注册,那么还需要考虑业务侧事件执行后销毁,避免重复触发

有没有办法可以减轻业务侧的心智负担呢?

办法总比困难多嘛~

我们可以使用一个全局变量 PostMessageCallBackMap 来存放注册的事件及回调,Promise 中的 resolve 正好有阅后即焚的特性,那么我们是不是可以考虑把创建出来的 resolve 作为 callback 放到 Map 中呢?

想到就立即行动!

实现

改造后的代码如下:

export const PostMessageCallBackMap = new Map();
const messageEventHandler = (event: MessageEvent) => {
  // 事件分发
  const data = event.data;
  let eventKey = data?.api;

  if (PostMessageCallBackMap.has(eventKey)) {
    PostMessageCallBackMap.get(eventKey)(data);
  }
};

// 接收
window.addEventListener("message", messageEventHandler);

接收我们写好了,发送这块怎么实现呢?

// 发送
function sendMessage<T>(param: JSBridgeReq): Promise<JSBridgeRes<T>> {
  return new Promise((resolve, reject) => {
    if (window.parent) {
      window.parent.postMessage(param, "*");
      // 将当前的 resolve 添加到 Map 中,等待返回事件触发
      let eventKey = param.api;
      PostMessageCallBackMap.set(eventKey, resolve);
    }
  });
}
// 这样封装一下,业务中使用就十分方便了
const { token } = await sendMessage({ api: "getLoginToken" })
console.log(token);

这样看起来舒服了很多,业务中应用起来也顺手了很多,完结撒花~

等等!

还有个问题呐。

相同的事件多次发起,应该如何处理响应结果?

陷入沉思

emm,按上面的写法,多次发送 getLoginToken 事件,Map 中的 Key 是唯一的,之前的事件会被覆盖掉,再加上异步事件返回时间不确定的话,完蛋了啊!

摆烂!

注释加上,不要调多次!

结束!

测试:哦?是吗?(狂点、狂点、狂点)

好啦好啦,虽然我们可以通过防抖来避免测试疯狂轰炸,但是某些场景下真的会有连续触发 api 的情况,那我们怎么解决呢?

每一个回调给他一个唯一id,然后通过事件id来进行匹配,不管事件执行的时间长短,通过事件id总可以找到另一半。

唯一id,最简单的我们就用时间戳吧。

改造!改造!改造!

export const PostMessageCallBackMap = new Map();

// 发送
function sendMessage<T>(param: JSBridgeReq): Promise<JSBridgeRes<T>> {
  return new Promise((resolve, reject) => {
    if (window.parent) {
      param.timestamp = Date.now();
      window.parent.postMessage(param, "*");
      
      let eventKey = param.api + param.timestamp;
      PostMessageCallBackMap.set(eventKey, resolve);
    }
  });
}

// 接收
const messageEventHandler = (event: MessageEvent) => {
  // 事件分发
  const data = event.data;
  let eventKey = data?.api;
  // timestamp 来保证对应关系
  eventKey = data?.api + (data?.timestamp || "");

  if (PostMessageCallBackMap.has(eventKey)) {
    PostMessageCallBackMap.get(eventKey)(data);
    // 由于事件id的存在,Map会越来越大,清除字典防止内存泄漏
    setTimeout(() => {
      PostMessageCallBackMap.delete(eventKey);
    }, 0);
  }
};
window.addEventListener("message", messageEventHandler);

总结

通过这样一番分析改造,终于让难以控制的全局注册事件 postMessage 变成了可以链式调用的 Promise,又可以愉快的写业务代码啦~

演示地址:https://ztstory.github.io/vue-composition-demo/#/PostMessagePromise

源码地址:https://github.com/ZTStory/vue-composition-demo

到此这篇关于postMessage消息通信Promise化的方法实现的文章就介绍到这了,更多相关postMessage消息Promise化内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • javascript 解决浏览器不支持的问题

    javascript 解决浏览器不支持的问题

    这篇文章主要介绍了javascript 解决浏览器不支持的问题的相关资料,需要的朋友可以参考下
    2016-09-09
  • 通过javascript的匿名函数来分析几段简单有趣的代码

    通过javascript的匿名函数来分析几段简单有趣的代码

    想起自己很久以前学习javascript的经历,也曾经碰到过几个由匿名函数造成的困扰(其中一个就是由闭包引起的),下面就整理几段简单代码讨论一下,让我们大家一起进步。
    2010-06-06
  • 微信小程序实现页面浮动导航

    微信小程序实现页面浮动导航

    这篇文章主要为大家详细介绍了微信小程序实现页面浮动导航,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-01-01
  • 小程序实现简单分页组件

    小程序实现简单分页组件

    这篇文章主要为大家详细介绍了小程序实现简单分页组件,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-08-08
  • 基于WebUploader的文件上传js插件

    基于WebUploader的文件上传js插件

    这篇文章主要为大家详细介绍了基于WebUploader的文件上传js插件,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-08-08
  • 详解TypeScript中类的定义与用法

    详解TypeScript中类的定义与用法

    TypeScript是一种开源的编程语言,它是JavaScript的超集,这篇文章主要来和大家介绍一下TypeScript中类的定义与用法,感兴趣的小伙伴可以了解一下
    2023-06-06
  • echarts多条折线图动态分层的实现方法

    echarts多条折线图动态分层的实现方法

    这篇文章主要介绍了echarts多条折线图动态分层的实现方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-05-05
  • javascript各种复制代码收集

    javascript各种复制代码收集

    javascript各种形式的复制代码效果,有直接复制url,复制文本框中的内容、隐藏表单中的内容,复制span中的内容
    2008-09-09
  • 原生js实现可拖拽效果

    原生js实现可拖拽效果

    本文主要介绍了原生js实现可拖拽效果的实例,具有很好的参考价值,下面跟着小编一起来看下吧
    2017-02-02
  • 微信小程序实现聊天对话(文本、图片)功能

    微信小程序实现聊天对话(文本、图片)功能

    这篇文章主要为大家详细介绍了微信小程序实现聊天对话功能,可以发送文本、图片,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-07-07

最新评论