diff --git a/packages/duplex-message/README.md b/packages/duplex-message/README.md index 1d63e4d..d76a403 100644 --- a/packages/duplex-message/README.md +++ b/packages/duplex-message/README.md @@ -220,10 +220,21 @@ interface IAbstractHubOptions { * if not set, a random id will be generated */ instanceID?: string | null + /** + * timeout(milliseconds) for waiting heartbeat message, default 500ms + * 1. A heartbeat message will be sent to peer immediately when a request message is received + * and there is at least one handler for it. Or the `emit` method will catch a no handler error + * It has nothing to do with the time of handler execution, there is no timeout + * for handler execution + * 2. Normally, a heartbeat message will be sent to peer in less then 10 ms, + * but you may still need to set a longer timeout if browser is heavy loaded + * and the native apis are slow + */ + heartbeatTimeout?: number } // new an instance with options -const postMessageHub = new PostMessageHub({ instanceID: 'some-id' }) +const postMessageHub = new PostMessageHub({ instanceID: 'some-id', heartbeatTimeout: 1000 }) // or use default options const pageScriptMessageHub = new PageScriptMessageHub() @@ -567,6 +578,17 @@ enum EErrorCode { } ``` +## FAQ +### How to deal with the error `no corresponding handler found for method xxx`? +This may happen in the following situations: +1. the message was sent to the peer, but the peer has no handler for it. +2. the message was sent to the wrong peer, or there is no peer(or the peer has not been set / loaded correctly). + 1. the peer is not using the same class that current instance using + 2. the peer has not been loaded(e.g.: iframe not loaded, worker not started) + 3. the peer is using different channel(e.g.: none-match `keyPrefix` for `StorageMessageHub`, none-match `channelName` for `BroadcastMessageHub`) +3. The message was sent to the wrong peer: `emit` specifies a `to` property in the `methodName` object, but the peer does not have the same instance ID as the `to` property. +4. the runtime(browser/node) is heavy loaded, the native api is too slow, you may need to set [a longer `heartbeatTimeout` for the instance](#constructor) + ## Development ```sh @@ -583,6 +605,9 @@ pnpm test # build pnpm build + +# publish +pnpm publish -r ``` ## License diff --git a/packages/duplex-message/package.json b/packages/duplex-message/package.json index 40d6891..4ff69bf 100644 --- a/packages/duplex-message/package.json +++ b/packages/duplex-message/package.json @@ -1,6 +1,6 @@ { "name": "duplex-message", - "version": "2.0.0", + "version": "2.0.1", "description": "makes one way message responsive, enhance postMessage/storageEvent/chrome extension scripts", "main": "dist/index.umd.js", "module": "dist/index.es.js", diff --git a/packages/duplex-message/src/abstract.ts b/packages/duplex-message/src/abstract.ts index 55d1f59..5fb822b 100644 --- a/packages/duplex-message/src/abstract.ts +++ b/packages/duplex-message/src/abstract.ts @@ -114,13 +114,24 @@ const CONTINUE_INDICATOR = '--message-hub-to-be-continued--' /** * timeout for waiting heartbeat message */ -const HEARTBEAT_WAIT_TIMEOUT = 200 +const DEFAULT_HEARTBEAT_WAIT_TIMEOUT = 500 export interface IAbstractHubOptions { /** * custom instance id */ instanceID?: string | null + /** + * timeout(milliseconds) for waiting heartbeat message, default 500ms + * 1. A heartbeat message will be sent to peer immediately when a request message is received + * and there is at least one handler for it. Or the `emit` method will catch a no handler error + * It has nothing to do with the time of handler execution, there is no timeout + * for handler execution + * 2. Normally, a heartbeat message will be sent to peer in less then 10 ms, + * but you may still need to set a longer timeout if browser is heavy loaded + * and the native apis are slow + */ + heartbeatTimeout?: number } export abstract class AbstractHub { @@ -146,6 +157,11 @@ export abstract class AbstractHub { */ protected _designedResponse: Record + /** + * timeout for waiting heartbeat message + */ + protected _heartbeatTimeout: number + /** * inner props to store whether instance is destroyed */ @@ -160,6 +176,7 @@ export abstract class AbstractHub { this._responseCallbackMap = {} this._messageID = 0 this._designedResponse = {} + this._heartbeatTimeout = (options && options.heartbeatTimeout) || DEFAULT_HEARTBEAT_WAIT_TIMEOUT this.isDestroyed = false if (process.env.NODE_ENV !== 'production') { console.log(`[duplex-message] create instance of ${this.constructor.name}, instanceID: ${this.instanceID}`) @@ -421,7 +438,7 @@ export abstract class AbstractHub { methodName: string | IMethodNameConfig, ...args: any[] ) { - if (this.isDestroyed) throw new Error('instance has been destroyed') + this.checkInstance() const reqMsg = this.buildReqMessage(methodName, args) const result = new Promise((resolve, reject) => { // 0 for not match @@ -495,7 +512,7 @@ export abstract class AbstractHub { false, ) this.runResponseCallback(resp) - }, HEARTBEAT_WAIT_TIMEOUT) + }, this._heartbeatTimeout) } protected buildReqMessage( diff --git a/packages/duplex-message/test/post-message/frame/frame-source.ts b/packages/duplex-message/test/post-message/frame/frame-source.ts index 0370eff..7498469 100644 --- a/packages/duplex-message/test/post-message/frame/frame-source.ts +++ b/packages/duplex-message/test/post-message/frame/frame-source.ts @@ -4,7 +4,7 @@ import DemoWorker from './worker?worker' console.log('frame proxy postMessage', location.href) -const hub = new PostMessageHub; +const hub = new PostMessageHub({instanceID: 'iframe'}); const worker = new DemoWorker() diff --git a/packages/duplex-message/test/post-message/frame/index.tb.ts b/packages/duplex-message/test/post-message/frame/index.tb.ts index 5890588..3779242 100644 --- a/packages/duplex-message/test/post-message/frame/index.tb.ts +++ b/packages/duplex-message/test/post-message/frame/index.tb.ts @@ -24,7 +24,7 @@ describe('PostMessage for iframe', () => { await wait(1000); const frameWindow = frame.contentWindow as Window - const hub = new PostMessageHub() + const hub = new PostMessageHub({ heartbeatTimeout: 1000 }); const msg = await hub.emit(frameWindow, { methodName: 'greet', targetOrigin: '*' }, 'hello') expect(msg).toBe('hello') @@ -37,5 +37,18 @@ describe('PostMessage for iframe', () => { expect(() => hub.emit(frameWindow, { methodName: 'greet', targetOrigin: 'https://www.google.com/' }, 'hello')).rejects.toThrowError() }) + it('with instanceID', async () => { + const frame = createFrame(); + await wait(1000); + const frameWindow = frame.contentWindow as Window + + const hub = new PostMessageHub({ heartbeatTimeout: 1000 }); + + expect(hub.emit(frameWindow, { methodName: 'greet', to: 'ab', targetOrigin: '*' }, 'hello')).rejects.toThrowError() + const msg = await hub.emit(frameWindow, { methodName: 'greet', targetOrigin: '*' }, 'hello') + expect(msg).toBe('hello') + const msg2 = await hub.emit(frameWindow, { methodName: 'greet', to: 'iframe', targetOrigin: '*' }, 'hello') + expect(msg2).toBe('hello') + }) }) diff --git a/packages/simple-electron-ipc/package.json b/packages/simple-electron-ipc/package.json index 3f144ff..2a57462 100644 --- a/packages/simple-electron-ipc/package.json +++ b/packages/simple-electron-ipc/package.json @@ -1,6 +1,6 @@ { "name": "simple-electron-ipc", - "version": "2.0.0", + "version": "2.0.1", "description": "an easy way to use electron ipc, get a response via promise, event support progress feedback", "main": "dist/index.js", "typings": "dist/index.d.ts",