download
raw
5.44 kB
import { workerBootstrap } from './workerBootstrap.js'
import { defineMainThreadModule } from './mainThreadFallback.js'
import { supportsWorkers } from './supportsWorkers.js'
let _workerModuleId = 0
let _messageId = 0
let _allowInitAsString = false
const workers = Object.create(null)
const registeredModules = Object.create(null) //workerId -> Set<unregisterFn>
const openRequests = Object.create(null)
/**
* Define a module of code that will be executed with a web worker. This provides a simple
* interface for moving chunks of logic off the main thread, and managing their dependencies
* among one another.
*
* @param {object} options
* @param {function} options.init
* @param {array} [options.dependencies]
* @param {function} [options.getTransferables]
* @param {string} [options.name]
* @param {string} [options.workerId]
* @return {function(...[*]): {then}}
*/
export function defineWorkerModule(options) {
if ((!options || typeof options.init !== 'function') && !_allowInitAsString) {
throw new Error('requires `options.init` function')
}
let {dependencies, init, getTransferables, workerId} = options
const onMainThread = defineMainThreadModule(options)
if (workerId == null) {
workerId = '#default'
}
const id = `workerModule${++_workerModuleId}`
const name = options.name || id
let registrationPromise = null
dependencies = dependencies && dependencies.map(dep => {
// Wrap raw functions as worker modules with no dependencies
if (typeof dep === 'function' && !dep.workerModuleData) {
_allowInitAsString = true
dep = defineWorkerModule({
workerId,
name: `<${name}> function dependency: ${dep.name}`,
init: `function(){return (\n${stringifyFunction(dep)}\n)}`
})
_allowInitAsString = false
}
// Grab postable data for worker modules
if (dep && dep.workerModuleData) {
dep = dep.workerModuleData
}
return dep
})
function moduleFunc(...args) {
if (!supportsWorkers()) {
return onMainThread(...args)
}
// Register this module if needed
if (!registrationPromise) {
registrationPromise = callWorker(workerId,'registerModule', moduleFunc.workerModuleData)
const unregister = () => {
registrationPromise = null
registeredModules[workerId].delete(unregister)
}
;(registeredModules[workerId] || (registeredModules[workerId] = new Set())).add(unregister)
}
// Invoke the module, returning a promise
return registrationPromise.then(({isCallable}) => {
if (isCallable) {
return callWorker(workerId,'callModule', {id, args})
} else {
throw new Error('Worker module function was called but `init` did not return a callable function')
}
})
}
moduleFunc.workerModuleData = {
isWorkerModule: true,
id,
name,
dependencies,
init: stringifyFunction(init),
getTransferables: getTransferables && stringifyFunction(getTransferables)
}
moduleFunc.onMainThread = onMainThread;
return moduleFunc
}
/**
* Terminate an active Worker by a workerId that was passed to defineWorkerModule.
* This only terminates the Worker itself; the worker module will remain available
* and if you call it again its Worker will be respawned.
* @param {string} workerId
*/
export function terminateWorker(workerId) {
// Unregister all modules that were registered in that worker
if (registeredModules[workerId]) {
registeredModules[workerId].forEach(unregister => {
unregister()
})
}
// Terminate the Worker object
if (workers[workerId]) {
workers[workerId].terminate()
delete workers[workerId]
}
}
/**
* Stringifies a function into a form that can be deserialized in the worker
* @param fn
*/
export function stringifyFunction(fn) {
let str = fn.toString()
// If it was defined in object method/property format, it needs to be modified
if (!/^function/.test(str) && /^\w+\s*\(/.test(str)) {
str = 'function ' + str
}
return str
}
function getWorker(workerId) {
let worker = workers[workerId]
if (!worker) {
// Bootstrap the worker's content
const bootstrap = stringifyFunction(workerBootstrap)
// Create the worker from the bootstrap function content
worker = workers[workerId] = new Worker(
URL.createObjectURL(
new Blob(
[`/** Worker Module Bootstrap: ${workerId.replace(/\*/g, '')} **/\n\n;(${bootstrap})()`],
{type: 'application/javascript'}
)
)
)
// Single handler for response messages from the worker
worker.onmessage = e => {
const response = e.data
const msgId = response.messageId
const callback = openRequests[msgId]
if (!callback) {
throw new Error('WorkerModule response with empty or unknown messageId')
}
delete openRequests[msgId]
callback(response)
}
}
return worker
}
// Issue a call to the worker with a callback to handle the response
function callWorker(workerId, action, data) {
return new Promise((resolve, reject) => {
const messageId = ++_messageId
openRequests[messageId] = response => {
if (response.success) {
resolve(response.result)
} else {
reject(new Error(`Error in worker ${action} call: ${response.error}`))
}
}
getWorker(workerId).postMessage({
messageId,
action,
data
})
})
}

Xet Storage Details

Size:
5.44 kB
·
Xet hash:
99237b95dd2e014e16aab0117e761cd0b70072bd02512cd66a492b05bb0e7654

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.