我正在尝试卸载阻塞我的 UI 的长时间运行的进程。
WebWorker 方法似乎是解决这种情况的最佳方法。
但是,我需要使用的库之一具有 async/await。
WebWorker 的 JS API 有限,并且似乎没有 async/await。
有没有办法在 WebWorker 中使用 Promise?
错误
ReferenceError:__awaiter 未定义
问候,
丹尼尔
更新:
添加__awaiter导致Promise不被识别。
var __awaiter =
(this && this.__awaiter) ||
function(thisArg, _arguments, Promise, generator) {
return new Promise(function(resolve, reject) {
generator = generator.call(thisArg, _arguments);
function cast(value) {
return value instanceof Promise && value.constructor === Promise
? value
: new Promise(function(resolve) {
resolve(value);
});
}
function onfulfill(value) {
try {
step('next', value);
} catch (e) {
reject(e);
}
}
function onreject(value) {
try {
step('throw', value);
} catch (e) {
reject(e);
}
}
function step(verb, value) {
var result = generator[verb](value);
result.done
? resolve(result.value)
: cast(result.value).then(onfulfill, onreject);
}
step('next', void 0);
});
}
这是如何构建 Web Worker 的骨架代码。
/* eslint-disable */
export default function ModelWorker() {
this.window = this
onmessage = async (e) => {
console.log(e);
}
}
WorkerProxy.js
export default class WorkerProxy {
constructor(worker) {
const code = worker.toString()
const src = code.substring(code.indexOf('{') + 1, code.lastIndexOf('}'))
const blob = new Blob([src], { type: 'application/javascript' })
return new Worker(URL.createObjectURL(blob))
}
}
SomeComponent.js
import WorkerProxy from './WorkerProxy'
import ModelWorker from './ModelWorker'
if (!!window.Worker) {
const worker = new WorkerProxy(ModelWorker)
worker.addEventListener('message', e => console.log(e.data))
worker.postMessage({ message:message })
// Load labels, etc.
}
async
/await
是ECMAScript语言的一部分,它们在所有全局范围内可用,无论是Window、Web Worker、Service Worker、Audio Worklet、Paint Worklet等。
这些范围可能没有一些 Web API,例如 DOM API、MediaDevices API、Geolocation API 等。但是,只要浏览器支持此 ECMAScript 功能,那么所有范围都将支持。
所以我不知道你的问题是什么,但绝对可以在 Worker 中使用 Promises 和 async/await 。
const worker = new Worker(
URL.createObjectURL(
new Blob([worker_script.textContent])
)
);
worker.onmessage = ({data}) => console.log(data);
<script type="worker-script" id="worker_script">
(async function() {
postMessage(['starting', performance.now()]);
await wait(2000);
postMessage(['ended', performance.now()]);
})();
function wait(time) {
return new Promise((res)=>setTimeout(res, time));
}
</script>
您绝对可以在 WebWorkers 中以及在 WebWorkers 中使用 async-await。
如果任何老式浏览器不支持 async-await 关键字,
你可以用 Promise 将它们转译为 ES5。
这甚至适用于 IE11。
这是一个关于如何操作的示例。
它使用await 调用辅助函数,并期望结果。
在 processData 中,worker 创建 1 到 5 秒之间的随机延迟,然后返回您传递的数据。 50% 的情况下(如 getRandomInt(1,2) ),它将返回异常,因此您会看到它也处理异常。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<title>Whitespace Removal Benchmark</title>
<style>
.loader
{
width: 30px;
aspect-ratio: 4;
--_g: no-repeat radial-gradient(circle closest-side,#000 90%,#0000);
background: var(--_g) 0% 50%, var(--_g) 50% 50%, var(--_g) 100% 50%;
background-size: calc(100%/3) 100%;
animation: l7 1s infinite linear;
}
@keyframes l7
{
33%
{
background-size: calc(100%/3) 0%,calc(100%/3) 100%,calc(100%/3) 100%
}
50%
{
background-size: calc(100%/3) 100%,calc(100%/3) 0%,calc(100%/3) 100%
}
66%
{
background-size: calc(100%/3) 100%,calc(100%/3) 100%,calc(100%/3) 0%
}
}
</style>
</head>
<body>
<h1>Whitespace Removal Benchmark</h1>
<div id="result">
<div class="loader" style="display: inline-block;"></div>
<div style="display: inline-block; font-size: 5mm;" class="loading">Benchmark running </div>
<div class="loader" style="display: inline-block;"></div>
</div>
<!--
https://css-loaders.com/dots/
-->
<script>
const workerCode = `"use strict";
function cryptoRand()
{
const randomBuffer = new Uint32Array(1);
(crypto || msCrypto).getRandomValues(randomBuffer);
return ( randomBuffer[0] / (0xffffffff + 1) );
}
function getRandomInt(min, max)
{
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(cryptoRand() * (max - min + 1)) + min;
}
function sleep(interval)
{
return new Promise(
function (resolve, reject)
{
let wait = setTimeout(
function ()
{
clearTimeout(wait);
if(getRandomInt(1,2) % 2 == 0)
resolve();
else
reject(new Error("Promise timed out ! "));
}
, interval
);
});
}
async function processData(data)
{
let interval = getRandomInt(1000, 5000);
await sleep(interval);
return data;
}
async function handleSomeTask(id, data)
{
try
{
// Process the data
const result = await processData(data);
// throw new Error("hello");
self.postMessage({ "id": id, "err":null, "result": result });
}
catch (err)
{
self.postMessage({ "id": id, "err":err, "result": null });
}
}
self.onmessage = function(event)
{
const { id, data } = event.data;
handleSomeTask(id, data);
};
`;
const blob = new Blob([workerCode], { type: 'text/javascript' });
const worker = new Worker(URL.createObjectURL(blob));
worker.onmessage = function(event)
{
const { id, err, result } = event.data;
// console.log("received", event.data);
if (err != null)
{
const reject = worker.workerErrorCallbacks.get(id);
if (reject)
{
reject(err);
worker.workerSuccessCallbacks.delete(id);
worker.workerErrorCallbacks.delete(id);
}
}
else
{
const resolve = worker.workerSuccessCallbacks.get(id);
if (resolve)
{
resolve(result);
worker.workerSuccessCallbacks.delete(id);
worker.workerErrorCallbacks.delete(id);
}
}
};
function generateRandom32BitInteger()
{
var array = new Int8Array(4);
(window.crypto || window.msCrypto).getRandomValues(array);
var dataView = new DataView(array.buffer);
var uint = dataView.getUint32();
// var f = uint / (0xffffffff + 1); // 0xFFFFFFFF = uint32.MaxValue (+1 because Math.random is inclusive of 0, but not 1)
// return f;
return uint;
}
function generateRandom128BitInteger()
{
const array = new Uint8Array(16);
crypto.getRandomValues(array);
let value = 0n;
for (let i = 0; i < array.length; i++)
{
value = (value << 8n) + BigInt(array[i]);
}
return value;
}
function generateRandomUuid()
{
const array = new Uint8Array(16);
crypto.getRandomValues(array);
let value = 0n;
for (let i = 0; i < array.length; i++)
{
value = (value << 8n) + BigInt(array[i]);
}
// return value;
const hexString = value.toString(16).padStart(32, '0');
// Format the hex string into UUID format
const uuid = `${hexString.slice(0, 8)}-${hexString.slice(8, 12)}-${hexString.slice(12, 16)}-${hexString.slice(16, 20)}-${hexString.slice(20)}`;
return uuid;
}
async function callWorkerFunction(worker, data)
{
if (!worker.workerSuccessCallbacks)
{
worker.workerSuccessCallbacks = new Map();
worker.workerErrorCallbacks = new Map();
// worker.callbackId = 0;;
}
// const id = Math.random(); // Generate a unique identifier
// const id = generateRandomUuid();
// const id = generateRandom128BitInteger();
const id = generateRandom32BitInteger();
// const id = worker.callbackId++;
return new Promise(
function (resolve, reject)
{
// worker.onmessage = function(event)
// {
// console.log("received event", event.data);
// if (event.data.id === id)
// {
// resolve(event.data.result);
// // worker.terminate(); // Optional: Terminate the worker after the result
// }
// };
worker.workerSuccessCallbacks.set(id, resolve);
worker.workerErrorCallbacks.set(id, reject);
worker.postMessage({ id, data });
}
);
}
function generateRandomString(min, max)
{
const length = Math.floor(Math.random() * (max - min + 1)) + min;
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
const charactersLength = characters.length;
let result = [];
for (let i = 0; i < length; i++)
{
result.push(characters.charAt(Math.floor(Math.random() * charactersLength)));
}
return result.join("");
}
async function testCall()
{
try
{
const randomString = generateRandomString(5, 15);
let passedData = { "hello": "kitty", "foo": "bar", "something": randomString };
const result = await callWorkerFunction(worker, passedData);
console.log("result for " + randomString + ":", result);
}
catch (err)
{
console.log("failed:", err);
}
}
function test()
{
for (let i = 0; i < 20; ++i)
{
testCall();
}
}
test();
</script>
</body>
</html>