@dandre3000/async-message
v0.2.0
Published
Call functions through Workers, Windows, MessagePorts and BroadcastChannels asynchronously.
Downloads
367
Maintainers
Readme
async-message
Call functions through Workers, Windows, MessagePorts and BroadcastChannels asynchronously.
Installation
npm i @dandre3000/async-message
Usage
Thread to thread
import { isMainThread } from '@dandre3000/is-main-thread'
import { expose, invoke } from '@dandre3000/async-message'
const main = async () => {
if (isMainThread) {
const url = new URL(import.meta.url)
const worker = globalThis.Worker ?
new Worker(url, { type: 'module' }) :
new (await import('node:worker_threads')).Worker(url)
expose(worker, 'call main', () => 'main')
console.log(await invoke(worker, 'call worker')) // worker
} else {
const parentPort = globalThis.Worker ?
globalThis :
(await import('node:worker_threads')).parentPort
expose(parentPort, 'call worker', () => 'worker')
console.log(await invoke(parentPort, 'call main')) // main
}
}
main()Tab to tab on a web browser
import { expose, invoke } from '@dandre3000/async-message'
const getName = () => name
if (opener) {
name = '2nd tab'
expose(opener, 'get name', getName)
onclick = async e => {
console.log(await invoke(window, 'get name')) // 1st tab
}
} else {
name = '1st tab'
const popup = open(location.href)
onclick = async e => {
console.log(await invoke(window, 'get name')) // 2nd tab
}
expose(popup, 'get name', getName)
}BroadcastChannel
import { isMainThread } from '@dandre3000/is-main-thread'
import { expose, invoke } from '@dandre3000/async-message'
const main = async () => {
const broadcastChannel = new BroadcastChannel('test')
const responseCount = 3
let count = 0
expose(broadcastChannel, 'count', () => ++count)
if (isMainThread) {
const workers = []
const url = new URL(import.meta.url)
if (globalThis.Worker) {
for (let i = 0; i < responseCount; i++) {
const worker = new Worker(url, { type: 'module' })
await invoke(worker, 'ping') // confirm worker is ready
workers.push(worker)
}
} else {
const Worker = (await import('node:worker_threads')).Worker
for (let i = 0; i < responseCount; i++) {
const worker = new Worker(url)
await invoke(worker, 'ping') // confirm worker is ready
workers.push(worker)
}
}
invoke(broadcastChannel, 'test')
} else {
const parentPort = globalThis.Worker ?
globalThis :
(await import('node:worker_threads')).parentPort
const ping = () => true
expose(parentPort, 'ping', ping)
expose(broadcastChannel, 'test', () => {
invoke(broadcastChannel, 'count', { responseCount }).then(v => {
console.log(count, ...v)
// 2 1 1 1
// 2 1 2 2
// 2 2 2 3
})
})
}
}
main()Transfer
import { isMainThread } from '@dandre3000/is-main-thread'
import { expose, invoke } from '@dandre3000/async-message'
const main = async () => {
if (isMainThread) {
const url = new URL(import.meta.url)
const worker = globalThis.Worker ?
new Worker(url, { type: 'module' }) :
new (await import('node:worker_threads')).Worker(url)
const { port1, port2 } = new MessageChannel
await invoke(worker, 'set port', { args: [port2], transferList: [port2] })
console.log(await invoke(port1, 'ping')) // true
} else {
const parentPort = globalThis.Worker ?
globalThis :
(await import('node:worker_threads')).parentPort
expose(parentPort, 'set port', port => {
expose(port, 'ping', () => true)
})
}
}
main()Exports
expose (port: Port, id: string, fn: (...args: any[]) => any, origin?: string | URL): void
Set up a function that will be called whenever invoke is called with a MessagePort bound to the given port and the specified id.
unexpose (port: Port, id: string, fn: (...args: any[]) => void): boolean
Remove a function from the list of functions that added to a MessagePort using expose.
Call an exposed function through a MessagePort and return a promise for the value returned by that function.
clear: (port: Port) => void
For the given port reject all unfulfilled Promises returned from invoke, remove all exposed functions and remove the message listener created by this module.
