簡體   English   中英

我可以構建一個執行任意 Javascript 代碼的 WebWorker 嗎?

[英]Can I build a WebWorker that executes arbitrary Javascript code?

我想在WebWorker API上構建一個抽象層,允許 (1) 在 webworker 上執行任意函數,以及 (2) 將交互包裝在 Promise 中。 在高層次上,這看起來像這樣:

function bake() {
  ... // expensive calculation
  return 'mmmm, pizza'
}

async function handlePizzaButtonClick() {
  const pizza = await workIt(bake)
  eat(pizza)
}

(顯然,可以毫不費力地添加帶有參數的方法。)

我的第一次workIt是這樣的:

async function workIt<T>(f: () => T): Promise<T> {
  const worker: Worker = new Worker('./unicorn.js') // no such worker, yet
  worker.postMessage(f)
  return new Promise<T>((resolve, reject) => {
    worker.onmessage = ({data}: MessageEvent) => resolve(data)
    worker.onerror   = ({error}: ErrorEvent)  => reject(error)
  })
}

這失敗了,因為函數不是結構化可克隆的,因此不能在工作消息中傳遞 (Promise 包裝器部分工作正常。)

序列化 Javascript 函數有多種選擇,有些比其他的更可怕。 但在我走那條路之前,我在這里錯過了什么嗎? 是否有另一種方法可以利用 WebWorker(或在單獨線程中執行的任何東西)來運行任意 Javascript?

除了我的評論之外,我認為一個示例會很有用,所以這里有一個基本的(沒有錯誤處理等)、自包含的示例,它從對象 URL加載工作人員:

Meta:我沒有將它發布在可運行的代碼片段視圖中,因為渲染的 iframe 在不同的來源運行(在我寫這個答案時https://stacksnippets.net - 請參閱片段輸出),這會阻止成功:在 Chrome ,我收到錯誤消息Refused to cross-origin redirects of the top-level worker script. .

無論如何,您可以復制文本內容,將其粘貼到此頁面上的開發工具 JS 控制台中,然后執行它以查看它是否有效。 而且,當然,它可以在同源上下文中的普通模塊中工作。

 console.log(new URL(window.location.href).origin);

// Example candidate function:
// - pure
// - uses only syntax which is legal in worker module scope
async function get100LesserRandoms () {
  // If `getRandomAsync` were defined outside the function,
  // then this function would no longer be pure (it would be a closure)
  // and `getRandomAsync` would need to be a function accessible from
  // the scope of the `message` event handler within the worker
  // else a `ReferenceError` would be thrown upon invocation
  const getRandomAsync = () => Promise.resolve(Math.random());

  const result = [];

  while (result.length < 100) {
    const n = await getRandomAsync();
    if (n < 0.5) result.push(n);
  }

  return result;
}

const workerModuleText =
  `self.addEventListener('message', async ({data: {id, fn}}) => self.postMessage({id, value: await eval(\`(\${fn})\`)()}));`;

const workerModuleSpecifier = URL.createObjectURL(
  new Blob([workerModuleText], {type: 'text/javascript'}),
);

const worker = new Worker(workerModuleSpecifier, {type: 'module'});

worker.addEventListener('message', ({data: {id, value}}) => {
  worker.dispatchEvent(new CustomEvent(id, {detail: value}));
});

function notOnMyThread (fn) {
  return new Promise(resolve => {
    const id = window.crypto.randomUUID();
    worker.addEventListener(id, ({detail}) => resolve(detail), {once: true});
    worker.postMessage({id, fn: fn.toString()});
  });
}

async function main () {
  const lesserRandoms = await notOnMyThread(get100LesserRandoms);
  console.log(lesserRandoms);
}

main();

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM