Javascript WebWorker - 异步/等待

问题描述 投票:0回答:2

我正在尝试卸载阻塞我的 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.
}
javascript async-await web-worker
2个回答
8
投票

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>


0
投票

您绝对可以在 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&nbsp;</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>
© www.soinside.com 2019 - 2024. All rights reserved.