Running with Code Like with scissors, only more dangerous

15Mar/130

A Loop for WinJS Promises

Posted by Rob Paveza

I had a co-worker ping me from an internal mailing list recently because he was having trouble reading from a network stream. I dug this out of mothballs from Win8 Consumer Preview:

WinJS.Class.define('Game', function() { /* constructor */ },
    { /* instance prototype */ },
    { /* static members */ 
        LoopWhileTrue: function (context, testCallback, actionReturningPromise, state) {
            /// <summary>Loops an action (that returns a promise) until another callback (that returns a Boolean) indicates that it should no longer continue.</summary>
            return new WinJS.Promise(function (success, error) {
                function evalActContinueLoop() {
                    if (testCallback.call(context, state)) {
                        var innerPromise = actionReturningPromise.call(context, state);
                        innerPromise.then(evalActContinueLoop, function (e) {
                            error(e);
                        });
                    }
                    else {
                        success(state);
                    }
               }
                evalActContinueLoop();
            });
        }
    });

This code was used for managing game state in a card-playing game app that I never finished. Usage might look like this:

var gameplay = Game.LoopWhileTrue(game, game.isGameInPlay, game.playHand, game.state);

gameplay’ then became a Promise that:

  • is fulfilled when the entire game completes (all hands have been played or a terminal score is achieved, based on the value returned by calling game.isGameInPlay(state)).
  • results in an error-fulfillment state if an internal call results in an error.
  • iteratively calls game.playHand(game.state), passing in ‘game’ as the this pointer (important in JS) until it is fulfilled (see 1st bullet).
  • if game.playHand completes synchronously, this method could cause a stack overflow. This can be broken with periodic use of setImmediate.

This method can conveniently be used to emulate a for-loop or a while-loop. It doesn't quite as well emulate a do-while-loop because it checks the terminal condition, but you could write a simple adapter method to always return true for the first call to the method.