Ostatnio bawię się Workerami, rozpracowują uruchamianie pętli z możliwością jej przerwania. Nie chodzi o brutalne uwalenie wątku, tylko o to, że ja między iteracjami jest sprawdzenie, czy pętla ma się zakończyć, czy kontynuować (oczywiście z zachowaniem warunków pętli takich, jak liczba iteracji). Testuję na bardzo prostym przykładzie. Jest pętla, która ma 10 iteracji, każda iteracja trwa pół sekundy (wykonuje pustą pętlę z pomiarem czasu, ale jest to symulacja jakiejś długotrwałej operacji). Przycisk Start uruchamia proces, ale przycisk Break przerywa proces, oczywiście przerwanie następuje między iteracjami. Postęp pokazuje się w konsoli.

Opracowałem dwa warianty. Pierwszy wariant (nazwy zmiennych i funkcji od Worker1) jest w stylu, w jakim stosuję w C# i C++ i tam działa z powodzeniem, a tutaj nie działa z tego powodu, że Worker podczas wykonywania funkcji nie jest w stanie natychmiast uruchomić innej funkcji. Drugi wariant (nazwy zmiennych i funkcji od Worker2) działa tak, jakbym chciał, czyli przycisk Break faktycznie zatrzymuje pętlę, ale jak widać kod jesst bardzo pokręcony.

Wystarczyłby jakikolwiek byt, który jest dostępny zarówno z Workera i z poza Workera i że jest to jeden i ten sam byt. Przekazywanie obiektów nie udało się, bo postMessage zawsze kopiuje obiekt, a localStorage ani sessionStorage nie istnieje w Worker. Czy da się i w jaki sposób poprawić pierwszy wariant, żeby można było przerwać pętlę, ale nie zagmatwać kodu tak, jak w drugim wariancie?

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <style type="text/css">
        </style>
    </head>
    <body>
        <input type="button" value="Start" onclick="Worker1Start()" />
        <input type="button" value="Break" onclick="Worker1Break()" />
        <br />
        <input type="button" value="Start" onclick="Worker2Start()" />
        <input type="button" value="Break" onclick="Worker2Break()" />
        <script type="text/javascript">

function Worker1Def()
{
    var Working;
    
    this.onmessage = function(Evt)
    {
        switch (Evt.data.Cmd)
        {
            case 0:
            {
                Start();
            }
            return;
            case 1:
            {
                Working = false;
                console.log("Break");
            }
        }
    }
    
    function Start()
    {
        console.log("Start");
        var I = 0;
        Working = true;
        while (Working)
        {
            console.log(I + " - begin");
            var T = performance.now();
            T = T + 500;
            while (T > performance.now())
            {
            }
            console.log(I + " - end");

            I++;
            if (I >= 10)
            {
                Working = false;
            }
        }
        console.log("Stop");
    }
}

var Worker1Obj = new Worker(URL.createObjectURL(new Blob(["("+Worker1Def.toString()+")()"], {type: 'text/javascript'})));

function Worker1Start()
{
    Worker1Obj.postMessage({Cmd:0});
}

function Worker1Break()
{
    Worker1Obj.postMessage({Cmd:1});
}

//////////////////////////////////////////////////////////////////////////////////////////////

function Worker2Def()
{
    this.onmessage = function(Evt)
    {
        switch (Evt.data.Cmd)
        {
            case 0:
            {
                Start();
            }
            return;
            case 1:
            {
                if (Evt.data.Val)
                {
                    Loop();
                }
                else
                {
                    console.log("Abort");
                }
            }
        }
    }
    
    var LoopI;
    
    function Start()
    {
        console.log("Start");
        LoopI = 0;
        Loop();
    }
    
    function Loop()
    {
        console.log(LoopI + " - begin");
        var T = performance.now();
        T = T + 500;
        while (T > performance.now())
        {
        }
        console.log(LoopI + " - end");
        
        LoopI++;
        if (LoopI < 10)
        {
            postMessage({Cmd:0})
        }
        else
        {
            console.log("Stop");
        }
    }
}

function Worker2DefCallback(Data)
{
    Worker2Obj.postMessage({Cmd:1,Val:Worker2Working});
}

var Worker2Working;

var Worker2Obj = new Worker(URL.createObjectURL(new Blob(["("+Worker2Def.toString()+")()"], {type: 'text/javascript'})));

Worker2Obj.onmessage = Worker2DefCallback;

function Worker2Start()
{
    Worker2Working = true;
    Worker2Obj.postMessage({Cmd:0});
}

function Worker2Break()
{
    Worker2Working = false;
}
        </script>
    </body>
</html>