Search code examples
javascriptrecursionweb-worker

Trying to implement inline Webworker for a recursive function


I have a first version of using a recursive function with Javascript which produces expected results. Below a working version :

// Call the recursive function and get final (a,b) results
var HitTemp = JSON.parse(JSON.stringify(HitCurrent));
var result= recursiveFunction(HitTemp, HitTemp.playerCurrent, maxNodes);
var a = HitTemp.coordPlayable[0];
var b = HitTemp.coordPlayable[1];

// Recursive function
function recursiveFunction(HitCurrent, colorCurrent, depth) {
 // Indices
 var i, j, k;
 // Evaluation
 var arrayTemp, eval, e;
 // Set current color to HitCurrent
 HitCurrent.playerCurrent = colorCurrent;
 // Deep copy of arrayCurrent array
 arrayTemp = JSON.parse(JSON.stringify(HitCurrent.arrayCurrent));
 // If depth equal to 0
 if (depth == 0)
  return evaluation(HitCurrent);
 // Starting evaluation
 eval = -infinity;
 // Browse all possible hits
 for (i = 0; i < 8; i++)
  for (j = 0; j < 8; j++) {
   if (HitCurrent.arrayPlayable[i][j] == 'playable') {
    for (k = 0; k < 8; k++) {
     // Explore line started from (i,j) with direction "k"
     exploreHitLine(HitCurrent, i, j, k, 'drawing');
    }
    // Recursive call
    e = recursiveFunction(JSON.parse(JSON.stringify(HitCurrent)), ((JSON.stringify(HitCurrent.playerCurrent) == JSON.stringify(playerBlack)) ? playerWhite : playerBlack), depth-1);
    if (e > eval) {
     HitCurrent.coordPlayable = [i,j];
     eval = e;
    }
   }
   // Restore arrayCurrent array
   HitCurrent.arrayCurrent = JSON.parse(JSON.stringify(arrayTemp));
   }
 return eval;
}

From this, I would like to use "inline" WebWorkers to dedicate the recursion to WebWorker and avoid hanging process in browsers.

I tried to follow this link and this other link

I don't know if I have to "postmessage" the object HitCurrent or the value eval to the main thread : by using WebWorker, I make confusions between the return instruction (which returns a value in the terminal case) and the objet HitCurrent argument passed for next recursive call.

If someone could give some clues to reproduce this original algorithm by using inline webworker (or with classical way of using webworker).


Solution

  • Inline webworker example:

    As your code, there is no function evaluation, function exploreHitLine. Before use following code, you must insert them into code.

    {
        let workerScript = URL.createObjectURL( new Blob( [ `
        "use strict";
        // Recursive function
        function recursiveFunction( HitCurrent, colorCurrent, depth ) {
            // Indices
            var i, j, k;
            // Evaluation
            var arrayTemp, eval, e;
            // Set current color to HitCurrent
            HitCurrent.playerCurrent = colorCurrent;
            // Deep copy of arrayCurrent array
            arrayTemp = JSON.parse(JSON.stringify(HitCurrent.arrayCurrent));
            // If depth equal to 0
            if ( depth === 0 ) return evaluation(HitCurrent);
            // Starting evaluation
            eval = -infinity;
            // Browse all possible hits
            for (i = 0; i < 8; i++) {
                for (j = 0; j < 8; j++) {
                    if (HitCurrent.arrayPlayable[i][j] === 'playable') {
                        for (k = 0; k < 8; k++) {
                            // Explore line started from (i,j) with direction "k"
                            exploreHitLine(HitCurrent, i, j, k, 'drawing');
                        }
                        // Recursive call
                        e = recursiveFunction(JSON.parse(JSON.stringify(HitCurrent)), ((JSON.stringify(HitCurrent.playerCurrent) == JSON.stringify(playerBlack)) ? playerWhite : playerBlack), depth-1);
                        if (e > eval) {
                            HitCurrent.coordPlayable = [i,j];
                            eval = e;
                        }
                    }
                    // Restore arrayCurrent array
                    HitCurrent.arrayCurrent = JSON.parse(JSON.stringify(arrayTemp));
                }
            }
            return eval;
        }
        onmessage = function ( event ) {
            let params = event.data;
            postMessage( { result: recursiveFunction( ...params ) } );
        }
            ` ], { type: "plain/text" } ) );
    
    
        // Call the recursive function and get final (a,b) results
        new Promise( resolve => {
            let HitTemp = JSON.parse(JSON.stringify(HitCurrent));
            let firstWorker = new Worker( workerScript );
            firstWorker.onmessage = function ( event ) {
                resolve( event.data ); //{ result: XXX }
            }
            firstWorker.postMessage( HitTemp, HitTemp.playerCurrent, maxNodes );
        } ).then( ( { result } ) => {
            let [ a, b ] = result.coordPlayable;
            console.log( result );
        } );
    }
    

    Additionally following is working inline WebWorker:

    {
        let workerScript = URL.createObjectURL( new Blob( [ `
        "use strict";
        onmessage = function ( event ) {
            let sum = 0, count = event.data;
            for ( let i = 0; i < count**count; i++ ) {
                sum += i;
            }
            postMessage( { result: sum, count } );
        }
        ` ], { type: "plain/text" } ) );
        let firstWorker = new Worker( workerScript );
        let firstAlive = setTimeout( () => {
            firstWorker.terminate();
            console.log( "terminated" );
        }, 3000 );
        firstWorker.onmessage = function ( event ) {
            clearTimeout( firstAlive );
            console.log( event.data );
        }
        firstWorker.postMessage( 10 );
    
        let secondWorker = new Worker( workerScript );
        let secondAlive = setTimeout( () => {
            secondWorker.terminate();
            console.log( "terminated" );
        }, 3000 );
        secondWorker.onmessage = function ( event ) {
            clearTimeout( secondAlive );
            console.log( event.data );
        }
        secondWorker.postMessage( 5 );
    
    }

    Update 1.

    {
        // Inline webworker version
        let workerScript = URL.createObjectURL( new Blob( [ `
        "use strict";
        // Recursive function
        function recursiveFunction( HitCurrent, colorCurrent, depth ) {
            // Indices
            var i, j, k;
            // Evaluation
            var arrayTemp, evaluated, e;
            // Set current color to HitCurrent
            HitCurrent.playerCurrent = colorCurrent;
            // Deep copy of arrayCurrent array
            arrayTemp = JSON.parse(JSON.stringify(HitCurrent.arrayCurrent));
            // If depth equal to 0
            if (depth == 0)
            return evaluation(HitCurrent);
            // Starting evaluation
            evaluated = -infinity;
            // Browse all possible hits
            for (i = 0; i < 8; i++) {
                for (j = 0; j < 8; j++) {
                    if (HitCurrent.arrayPlayable[i][j] == 'playable') {
                        for (k = 0; k < 8; k++) {
                            // Explore line started from (i,j) with direction "k"
                            exploreHitLine(HitCurrent, i, j, k, 'drawing');
                        }
                        // Recursive call
                        e = recursiveFunction(JSON.parse(JSON.stringify(HitCurrent)), ((JSON.stringify(HitCurrent.playerCurrent) == JSON.stringify(playerBlack)) ? playerWhite : playerBlack), depth-1);
                        if ( e > evaluated ) {
                            HitCurrent.coordPlayable = [i,j];
                            evaluated = e;
                        }
                        if (e == -infinity) { HitCurrent.coordPlayable = [ i, j ]; }
                        // Restore arrayCurrent array
                        HitCurrent.arrayCurrent = JSON.parse(JSON.stringify(arrayTemp));
                    }
                }
            }
            return evaluated;
        }
    
        onmessage = function ( event ) {
            let params = event.data;
            //postMessage( { result: recursiveFunction(  HitCurrent, HitCurrent.playerCurrent, maxNodes ) } );
            postMessage( { result: recursiveFunction( ...params ) } );
        };
        ` ], { type: "plain/text" } ) );
    
    
       // Call the recursive function and get final (a,b) results
        new Promise( resolve => {
            let HitTemp = JSON.parse(JSON.stringify(HitCurrent));
            let firstWorker = new Worker( workerScript );
            firstWorker.onmessage = function ( event ) {
                resolve( event.data ); //{ result: XXX }
            }
            firstWorker.postMessage( [ HitTemp, HitTemp.playerCurrent, maxNodes ] );
        } ).then( ( { result } ) => {
            let [ a, b ] = result.coordPlayable;
            console.log( result );
        } );
    }

    Explanation of my faults:

    1. It is impossible to use "eval" as the name of a variable when in "strict mode".

    =>

    from: eval

    to: evaluated

    1. Worker.postMessage( aMessage, Transferrable ), in this case, you don't need to use the second parameter.

    =>

    from: firstWorker.postMessage( HitTemp, HitTemp.playerCurrent, maxNodes );

    to: firstWorker.postMessage( [ HitTemp, HitTemp.playerCurrent, maxNodes ] );

    (https://developer.mozilla.org/en-US/docs/Web/API/Worker/postMessage)

    1. Continue to 2, passing parameter to recursiveFunction is fixed.