Skip to content

Commit

Permalink
feat: add heartbeatTimeout to constructor #28
Browse files Browse the repository at this point in the history
  • Loading branch information
Saiya committed Aug 22, 2024
1 parent 5342ed9 commit cdaee6a
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 8 deletions.
27 changes: 26 additions & 1 deletion packages/duplex-message/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down Expand Up @@ -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
Expand All @@ -583,6 +605,9 @@ pnpm test

# build
pnpm build

# publish
pnpm publish -r
```

## License
Expand Down
2 changes: 1 addition & 1 deletion packages/duplex-message/package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
23 changes: 20 additions & 3 deletions packages/duplex-message/src/abstract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -146,6 +157,11 @@ export abstract class AbstractHub {
*/
protected _designedResponse: Record<string, string>

/**
* timeout for waiting heartbeat message
*/
protected _heartbeatTimeout: number

/**
* inner props to store whether instance is destroyed
*/
Expand All @@ -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}`)
Expand Down Expand Up @@ -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<ResponseType>((resolve, reject) => {
// 0 for not match
Expand Down Expand Up @@ -495,7 +512,7 @@ export abstract class AbstractHub {
false,
)
this.runResponseCallback(resp)
}, HEARTBEAT_WAIT_TIMEOUT)
}, this._heartbeatTimeout)
}

protected buildReqMessage(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down
15 changes: 14 additions & 1 deletion packages/duplex-message/test/post-message/frame/index.tb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand All @@ -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')
})
})

2 changes: 1 addition & 1 deletion packages/simple-electron-ipc/package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down

0 comments on commit cdaee6a

Please # to comment.