30分钟简单实现一下postMessage的Promise业务场景

1,469 阅读1分钟

背景

项目中接入三维模型,由于三维模型团队只提供iframe方式接入和postMessage通讯方式和通讯的数据格式。

// api调用的接口
// params 传递参数
{
    require: {
        api: Strubg,
        params: *
    }
}

刚开始的样子

   <div class="model">
       <iframe src="xxx" ref="iframe" />
   </div>
created() {
    this.initEvent()
},
methods: {
    initEvent() {
        window.addEventListener(
        'message',
        this.handleReceive,
        false
      )

      this.$once('hook:beforeDestroy', () => {
        window.removeEventListener(
          'message',
          this.handleReceive,
          false
        )
      })
    },
    // 接收事件
    handleReceive(e) {
        console.log(e)
        // e.data 就是接收的数据
    },
    // 发送事件
    postMessage({ api, params }) {
        const mergeParams = {
            require: {
                api,
                params
            }
        }
        this.$refs.iframe.contentWindow.postMessage(mergeParams, '*')
    }
}
// 使用的时候
this.postMessage({
    api: 'setData'
})

简单实现一下,但是发现不能直接拿到调用接口的返回值,只能通过handleReceive接收值,并回调,这样子很麻烦。

promise改造

思路整理

image.png

需要跟三维模型团队约定uuid。

// queue.js
/**
 * 异步队列
 * @param time - 超时时间,单位秒
 */
export default class QueuePromise {
  constructor(time = 15) {
    this.queue = new Map()
    this.time = time
  }

  // 加入队列
  push(uuid, resolve, reject) {
    const params = { uuid, resolve, reject }
    params.timer = setTimeout(() => {
      this.put(uuid)
      reject({ code: -1, msg: '超时' })
    }, this.time * 1000)
    this.queue.set(uuid, params)
  }

  // 执行队列,并出栈
  perform(uuid, params) {
    const apiQueue = this.put(uuid)
    // 这里执行resolve,并把结果返回回去
    apiQueue.resolve(params)
  }

  // 找到并出栈
  put(uuid) {
    const apiQueue = this.queue.get(uuid)
    if (!apiQueue) return
    this.queue.delete(uuid)
    apiQueue.timer && clearTimeout(apiQueue.timer)
    return apiQueue
  }

  clear() {
    this.queue.forEach(e => {
      e.timer && clearTimeout(e.timer)
    })
    this.queue = null
  }
}
+ import Queue from './queue'

created() {
    this.initEvent()
},
methods: {
    initEvent() {
+       this.queue = new Queue()
        window.addEventListener(
            'message',
            this.handleReceive,
            false
        )

      this.$once('hook:beforeDestroy', () => {
        window.removeEventListener(
          'message',
          this.handleReceive,
          false
        )
+       this.queue && this.queue.clear()
      })
    },
    // 接收事件
    handleReceive(e) {
        console.log(e)
        // e.data 就是接收的数据
+      const { api, params, uuid } = e.data?.require || {}
+      if (uuid) return this.queue.perform(uuid, params) // 如果有uuid,则去执行resolve或者reject
        
        // 正常处理你的业务
    },
    // 发送事件
    postMessage({ api, params }) {
+       return new Promise((resolve, reject) => {
            const mergeParams = {
                require: {
                    api,
                    params
                }
            }
+           this.$refs.iframe.contentWindow.postMessage(mergeParams, '*')
+           this.queue.push(objRequest.require.uuid, resolve, reject)
+       })
    }
}
// 使用的时候
this.postMessage({
    api: 'setData'
}).then(e => { // 拿到返回值
    console.log(e)
})

// 或者
const res = await this.postMessage({
    api: 'setData'
})

以上就是postMessagePromise化全过程。