-1

I have a browser game that has to load some things before it starts. I have encapsulated all the loading into two promises.

var promise1 = new Promise(function(resolve, reject) { ...loading stuff here... resolve() });
var promise2 = new Promise(function(resolve, reject) { ...loading stuff here... resolve() });

Then I have put a function that starts the game loops in the following

Promise.all([promise1, promise2]).then(setInterval(GameLoop, time));

EDIT: adding () => to the above code hasn't resolved the issue.

The problem is that, although everything starts up and runs fine, it still throws four frames of errors before everything loads. I thought promises were supposed to wait until the code inside them did its thing.

I'm not really sure what's gone wrong. Probably some fundamental thing about javascript that I've missed. Browser isn't throwing any errors other than it can't find one variable for 4 - 5 frames right at the start.

Original code:

//{ DATA
    var promise_loadClient = new Promise ((resolve, reject) => {
    
        //{ LOAD DISPLAY OBJECTS
            //Objects required for displaying elements of the game
            window.canvasb = document.getElementById('screen_board');
            window.ctxb = canvasb.getContext('webgl',{ alpha: false });
            ctxb.clearColor(100.0, 0.0, 0.0, 1.0);
            canvasb.width = window.innerWidth;
            canvasb.height = window.innerHeight - element_navbar.offsetHeight;
            
            window.pixi_app = new PIXI.Application({
                view: canvasb,
                antialias: true,
                width: window.innerWidth,
                height: window.innerHeight - element_navbar.offsetHeight
            });
            
            window.graphics = new PIXI.Graphics();      
            
            window.canvas = document.getElementById('screen_entities');
            window.ctx = canvas.getContext('2d',{ alpha: true });
                //ctx.imageSmoothingEnabled = false;
                //ctx.webkitImageSmoothingEnabled = false;
                //ctx.mozImageSmoothingEnabled = false;
                ctx.translate(0.5,0.5);

        //}

        //{ GAME LOOP VARS
            window.step = 1; //frame time target (ms)
            window.timein = 0  //vars for recording loop time
            window.timeout = 0;
            window.averageTimes = [];
        //}

        //{ LOAD TEXT DISPLAY OBJECTS
            window.element_timer = document.getElementById('timer'); //Time repurposed as debug window
                   element_timer.style.left = -1000; //hide the debug window
            window.element_highscores = document.getElementById('highscores'); //High score display
            window.element_moveindicator = document.getElementById('moveindicator'); // Moves left indicator
        //}
    
        window.mouseX = 0; window.mouseY = 0; //TODO: embed in playerview
        resolve();
    });
//}

And

var promise_loadManager = new Promise ( (resolve, reject) => {
    //{ DATA LOAD

        //Images
        window.texture_blackrook = new PIXI.Texture.from('../assets/images/rookblack.png');
        window.texture_blackpawn = new PIXI.Texture.from('../assets/images/pawnblack.png');
        window.texture_blackking = new PIXI.Texture.from('../assets/images/kingblack.png');
        window.texture_blackqueen = new PIXI.Texture.from('../assets/images/queenblack.png');
        window.texture_blackbishop = new PIXI.Texture.from('../assets/images/bishopblack.png');
        window.texture_blackknight = new PIXI.Texture.from('../assets/images/knightblack.png');
        
        window.texture_whiterook = new PIXI.Texture.from('../assets/images/rookwhite.png');
        window.texture_whitepawn = new PIXI.Texture.from('../assets/images/pawnwhite.png');
        window.texture_whiteking = new PIXI.Texture.from('../assets/images/kingwhite.png');
        window.texture_whitequeen = new PIXI.Texture.from('../assets/images/queenwhite.png');
        window.texture_whitebishop = new PIXI.Texture.from('../assets/images/bishopwhite.png');
        window.texture_whiteknight = new PIXI.Texture.from('../assets/images/knightwhite.png');
        
        window.texture_greyrook = new PIXI.Texture.from('../assets/images/rookgrey.png');
        window.texture_greypawn = new PIXI.Texture.from('../assets/images/pawngrey.png');
        window.texture_greyking = new PIXI.Texture.from('../assets/images/kinggrey.png');
        window.texture_greyqueen = new PIXI.Texture.from('../assets/images/queengrey.png');
        window.texture_greybishop = new PIXI.Texture.from('../assets/images/bishopgrey.png');
        window.texture_greyknight = new PIXI.Texture.from('../assets/images/knightgrey.png');
        
        window.texture_whitearrow = new PIXI.Texture.from('../assets/images/arrow01.png');
        
        window.texture_tilelight = PIXI.Texture.from('../assets/images/tilelight.bmp');
        
        //{OLD IMAGES - TO BE REPLACED SOON
        window.img_blackking = new Image(1,1); img_blackking.src = '../assets/images/kingblack.png';
        window.img_blackqueen = new Image(1,1); img_blackqueen.src = '../assets/images/queenblack.png';
        window.img_blackbishop = new Image(1,1); img_blackbishop.src = '../assets/images/bishopblack.png';
        window.img_blackrook = new Image(1,1); img_blackrook.src = '../assets/images/rookblack.png';
        window.img_blackknight = new Image(1,1); img_blackknight.src = '../assets/images/knightblack.png';
        window.img_blackpawn = new Image(1,1); img_blackpawn.src = '../assets/images/pawnblack.png';
        
        window.img_whiteking = new Image(1,1); img_whiteking.src = '../assets/images/kingwhite.png';
        window.img_whitequeen = new Image(1,1); img_whitequeen.src = '../assets/images/queenwhite.png';
        window.img_whitebishop = new Image(1,1); img_whitebishop.src = '../assets/images/bishopwhite.png';
        window.img_whiterook = new Image(1,1); img_whiterook.src = '../assets/images/rookwhite.png';
        window.img_whiteknight = new Image(1,1); img_whiteknight.src = '../assets/images/knightwhite.png';
        window.img_whitepawn = new Image(1,1); img_whitepawn.src = '../assets/images/pawnwhite.png';
        
        window.img_greyking = new Image(1,1); img_greyking.src = '../assets/images/kinggrey.png';
        window.img_greyqueen = new Image(1,1); img_greyqueen.src = '../assets/images/queengrey.png';
        window.img_greybishop = new Image(1,1); img_greybishop.src = '../assets/images/bishopgrey.png';
        window.img_greyrook = new Image(1,1); img_greyrook.src = '../assets/images/rookgrey.png';
        window.img_greyknight = new Image(1,1); img_greyknight.src = '../assets/images/knightgrey.png';
        window.img_greypawn = new Image(1,1); img_greypawn.src = '../assets/images/pawngrey.png';
        
        window.img_whitearrow = new Image(1,1); img_whitearrow.src = '../assets/images/arrow01.png';
        //}
        
        //Sounds
        window.sound_swipe = new Audio("../assets/sounds/chess_swipe.mp3");
        window.sound_ticking = new Audio("../assets/sounds/chess_ticking3.mp3");
        window.sound_turnbell = new Audio("../assets/sounds/chess_softbell.mp3");
        window.sound_swish = new Audio ("../assets/sounds/chess_swish.mp3");
        window.sound_tap = new Audio ("../assets/sounds/chess_tap.mp3");
        
        
        //Fetch elements
        window.element_timer = document.getElementById('timer'); //TEMP RENDER TIMER
        window.element_highscores = document.getElementById('highscores'); //TEMP RENDER TIMER
        window.element_moveindicator = document.getElementById('moveindicator');
        
        // Env
        window.manager = new GameManager ("inactive", Date.now(), 10000); // Create manager, let it be altered later in the script


    //}

    //{ CONNECT TO SERVER
        
        
        window.socket = io('//localhost:3001'); 
    //}

    //{ PLAYER INFO
        //Information about the player's id and game choices.
        
        //{ Get room request from URL and send to server!
            window.roomRequest = window.location.search.split("=")[1];
            socket.emit("requestboard", parseInt(roomRequest));
        //}
        
        //{ Get nickname from cookies, if not there ask for one. To be replaced by user auth interface
            window.nickname = CookieIO.GetCookieByName("nickname");
            if(nickname == null)
            {
                nickname = window.prompt("Please enter a nickname. Max 16 characters. (To change nickname, you'll need to clear cookies for this site)");
                nickname = nickname.substring(0,16);
                CookieIO.SetCookieByName("nickname", nickname, 365); 
            }
        //}

    //}

    //{ SERVER LOAD
        //{ Setup game - create board from server details and recieve pieces information
            socket.on('newboard', function(msg){
            
                console.log("SERVER: New Board");

                //Create board object from server
                window.manager = new GameManager("inactive",0,0);
                window.board = new ChessBoard(msg.chessBoard.radius,
                                       msg.chessBoard.randomRange,
                                       msg.chessBoard.randomLimit,
                                       new HexColor(msg.chessBoard.colorLight),
                                       new HexColor(msg.chessBoard.colorDark),
                                       msg.chessBoard.shading_radius,
                                       msg.chessBoard.shading_innerStop,
                                       msg.chessBoard.shading_outerStop,
                                       msg.chessBoard.shading_innerAlpha,
                                       msg.chessBoard.shading_outerAlpha
                                       );
                //Precalculate as may variables as possible.
                window.boardSprites = new ObjectRegister(board.radius * 2, board.radius * 2);
                window.boardViable = new ObjectRegister(board.radius*2, board.radius * 2);          
                    PreCalcBoardSprites(boardSprites, board, texture_tilelight);
                    PreCalcTileRNGViable(boardViable);
                
                    
                
                window.client = new PlayerData(msg.playerID, msg.roomName);
                client.nickname = nickname;
                
                //and create a way to view it with player!
                window.player = new PlayerView(board.radius, board.radius,4);
                    player.viewportBoardMaxX = board.radius * 2;
                    player.viewportBoardMaxY = board.radius * 2;
                    player.viewportMoved = true;
                        
                window.pieces = new ChessPieceRegister(board.radius * 2, board.radius * 2);
                window.deadPieces = [];
                for(i = 0; i < msg.chessPieceRegister.length; i++){     
                    pieces.NewPiece(parseInt(msg.chessPieceRegister[i].positionX),
                                    parseInt(msg.chessPieceRegister[i].positionY), 
                                    msg.chessPieceRegister[i].type,
                                    msg.chessPieceRegister[i].team,
                                    msg.chessPieceRegister[i].playerID);
                }

                isLoaded = true; //Once these things are loaded, then the game should be good to go.
                //A board will display and the player can look around. 
            });
            socket.on('newplayerpieces', function (msg){
                console.log("SERVER: newplayerpieces");
                let tempPiece = null;
                for(i = 0; i < msg.length; i++)
                {   
                    tempPiece = pieces.GetPiece(parseInt(msg[i].positionX), parseInt(msg[i].positionY));
                    
                    if(tempPiece != null)
                    {
                        oldX = tempPiece.oldPositionX;
                        oldY = tempPiece.oldPositionY;
                        displayX = tempPiece.displayPositionX;
                        displayY = tempPiece.displayPositionY;
                        pieces.NewOldPiece(parseInt(msg[i].positionX),
                                                      parseInt(msg[i].positionY),
                                                      msg[i].type,
                                                      msg[i].team,
                                                      msg[i].playerID,
                                                      oldX,
                                                      oldY,
                                                      displayX,
                                                      displayY);
                    }
                    else
                    {
                        pieces.NewPiece(parseInt(msg[i].positionX), parseInt(msg[i].positionY), msg[i].type, msg[i].team, msg[i].playerID);
                    }
                }
                
                client.pieces = pieces.GetPiecesByID(client.playerID);
                kings = pieces.GetPiecesByIDType(client.playerID, "king");
                client.king = kings[0]; //fuck it, we'll only have the one king
                client.team = client.king.team;
                
                player.positionX = client.king.positionX; //Change to a lerp
                player.positionY = client.king.positionY;

                isLoadedPieces = true;

            }); 
        //}
    //}

    //{ SERVER IO
        //{ Message from server.
            socket.on('messagefromserver', function (msg){
            msgs = msg.split('/');
            
            if (msgs[0] == "alert"){
                window.alert(msgs[1]);
            } else {
                console.log("SERVER: Message from server: " + msgs[1]);
            }
        });
        //}

        //{ Game Management (turns)
            socket.on('newturn', function(msg){
            console.log("SERVER: new turn: " + msg + "localnumber: " + (manager.turnNumber + 1));
            //sound_turnbell.volume = 0.5;
            //sound_turnbell.play();
            
            msgs = msg.split("/");
            
            manager.turnStart = Date.now();
            manager.turnAge = parseInt(msgs[0]);        //0, usually
            manager.turn = msgs[1];         //Color
            manager.gameState = msgs[2];    //Active
            manager.turnTime = msgs[3];     //How long in ms (5000)
            manager.turnTimeMax = msgs[3];  // "
            manager.movesLeft = parseInt(msgs[4]) + Math.floor(MyMath.Clamp(-16 + client.pieces.length, 0 ,50) / 4);
            //viewport.viewportMoved = true;
            if(client.king != null) manager.actualScore ++;
            
            
            manager.turnNumber ++;
            
        });
        //}

        //{ Deal with player interactions
            socket.on('newmove', function(msg){
                console.log("SERVER: new move: " + msg);

                sound_swipe.play();
                
                
                moves = msg.split("/");
                let oldPiece = pieces.GetPiece(moves[3], moves[4]);
                piece = pieces.GetPiece(parseInt(moves[1]),parseInt(moves[2]));
                let killerPiece = piece;
                piece.oldMoveGizmoEnd = Date.now() + (manager.turnTimeMax * 3);
                piece.oldMoveGizmoStart = Date.now();
                piece.hasMovedThisTurn = true;
                pieces.MovePiece(piece, parseInt(moves[3]),parseInt(moves[4]));
                if(piece.playerID == client.playerID) manager.Move();


                
                if(oldPiece != null) {
                    deadPieces.push(new DeadPiece(oldPiece, 0, killerPiece));
                    manager.actualScore += oldPiece.rank;
                }
                    
                client.pieces = pieces.GetPiecesByID(client.playerID);
                player.viewportMoved = true;
            });
            socket.on('removeplayer', function(msg){
                console.log("SERVER: Remove Player: " + msg);
                pieces.RemovePiecesById(msg);
                player.viewportMoved = true;
            }); 
            socket.on('killplayer', function(msg){
                console.log("SERVER: Killplayer: " + msg);
                msgs = msg.split("/");
                pieces.KillPlayer(msgs[0], msgs[1], msgs[2]);
                player.viewportMoved = true;
                client.pieces = pieces.GetPiecesByID(client.playerID);
            });
            socket.on('changeteamofpiece', function (msg){
                
                    console.log("SERVER: change team of piece", msg);
                    msgs = msg.split("/");
                    pieces.ChangeTeamOfPiece(msgs[0], msgs[1], msgs[2], msgs[3]);
                    player.viewportMoved = true;
                });
            socket.on('newscores', function(msg) {
                console.log("SERVER: newscores: ", msg);
                msg.sort(function(a, b){return b.score-a.score});   
                manager.allScoresNew = msg;
                
            });
        //}

    //}
    
    window.gameInitialized = true
    resolve();
});

And

Promise.all([promise_loadClient, promise_loadManager]).then( () => setInterval(GameLoop, step) );
pretzelhammer
  • 13,874
  • 15
  • 47
  • 98
Colin Smith
  • 300
  • 2
  • 8
  • 1
    `.then()` expects a function and not a number -> `.then(() => setInterval(...))` – Andreas Jun 26 '20 at 15:07
  • 1
    `.then(setInterval(GameLoop, time));` this will *immediately execute*, it will not wait for the promises to finish. You need `.then(() => setInterval(GameLoop, time));` – VLAZ Jun 26 '20 at 15:08
  • 1
    If you show us your REAL code for loading things, we can help you fix it, but we need to see the actual code. Questions about code here should contain all the relevant real code, not a shortened version, not a pseudo-code example. That allows us to offer a full answer that contains all the things that need fixing, not only the things you think to ask about. From your pseudo-code example, it appears you may be calling `resolve()` before the loading is actually done. – jfriend00 Jun 26 '20 at 15:11
  • Thats the real code up. I thought it might be too long or something. Adding the '() =>' didn't work – Colin Smith Jun 26 '20 at 15:19
  • 1
    "the first [ edit ] disappeared somehow" ... https://stackoverflow.com/posts/62597586/revisions shows you what happened. – ADyson Jun 26 '20 at 15:24
  • 1
    ok so `promise_loadClient` doesn't appear to be doing anything obviously asynchronous - or if it is, you aren't handling any callbacks within it. So it's unclear what, if anything, it isn't waiting for in there - can you be more specific? – ADyson Jun 26 '20 at 15:27
  • 3
    `promise_loadManager` does do some async stuff with sockets. But again it's unclear which part(s) of it you're expecting to wait for, and anyway your `resolve();` command doesn't wait for any them to complete - it's called in the main body of the promise function, not in any of the callbacks which would occur as a result of a socket event. So it'll wait till you've set up those event handlers, but it won't wait till the events actually occur. Again, can you be more specific about what thing(s) you expect it to wait for? – ADyson Jun 26 '20 at 15:29
  • I think the problem is I expected it to wait for everything. I think this is the answer. I'm trying to wait until all the little bits I need are loaded before I start the game. I don't think this is the proper approach to use because to make it work I'd need to gaffer tape it with exactly the kind of code I was trying to avoid by doing this. – Colin Smith Jun 26 '20 at 15:41
  • What specifically in that code are you trying to wait for? You are currently waiting for everything to be registered (event handlers set up), but you aren't waiting for any of your event handlers to actually get called. We would need to know precisely and specifically what you are trying to wait for before your promise resolves? – jfriend00 Jun 26 '20 at 15:53
  • "I'd need to gaffer tape it with exactly the kind of code I was trying to avoid"...are you sure? Again, without knowing what thing(s) you're trying to wait for, it's tricky to say what precisely the correct approach is going to be. In general, wrapping callback-based code in Promises _can_ be a good approach because it can allow you to be more organised, to use async/await etc. But to know if what you're doing is possible / sensible, we do need some more specifics please. – ADyson Jun 26 '20 at 15:57
  • e.g. when you say "wait for everything", that doesn't sound likely or logical. You can't have one Promise wait for all "new turn" events. Even using this Promise to wait for a single "new turn" event wouldn't be all that logical if you're also trying to wait for the board setup event, new scores event etc etc. Maybe these should really be wrapped in separate Promises? Although that's a bit of a one-off thing. That would be ok for the initial setup event(s), if that's what you need? – ADyson Jun 26 '20 at 16:00
  • It might also make sense if `socket.on` returned a Promise itself rather than making you specify a callback. That would be a more "modern" approach. Since we don't know what the `socket` object actually is (e.g. is it from some library you've used, or something you created yourself?) we can't say for certain if it might already support that or not. – ADyson Jun 26 '20 at 16:01
  • 1
    Thanks ADyson. I needed to be specific - I'd wanted a way to surround everything and add and remove as I developed, but this wasn't going to work. I've put a promise around only "socket.on('newboard', function(msg){" and placed the resolve inside that function (and not at the end of the whole promise) so that it only fires once its loaded that stuff from the server. Fundamental misunderstanding of how js executes. Either way. Thats the answer, but its not an answer. Don't know if I should answer it myself or just delete, or rework the question to make the actual problem really obvious. – Colin Smith Jun 26 '20 at 16:07
  • 1
    It would be fine to answer it yourself, if you think it might help others in a similar position with understanding the concepts (which I think it might). P.S. the change to setInterval is also relevant and a necessary part of the solution too. – ADyson Jun 26 '20 at 16:14

2 Answers2

3

setInterval is executing right away. Try placing that code in a function that's passed to .then:

Promise.all([promise1, promise2]).then(() => setInterval(GameLoop, time));
zeterain
  • 1,140
  • 1
  • 10
  • 15
1

I have misunderstood promises.

Resolve is being called before the asynchronous functions inside the promise are done and this, in turn, means that the promise is finishing earlier than desired.

Essentially each promise as I had it before was:

var promise1 = new Promise ((resolve, reject) => {
    
    function irrelivantfunction1 (){
        // things
    }
    
    async function importantfunction1 (){
        // things
    }
    
    function irrelivantfunction2 (){
        // things
    }
    
    async function importantfunction2 (){
        // things
    }
    resolve()   

})

But it should be:

function irrelivantfunction1 (){
    // things
}

var promise1 = new Promise ((resolve, reject) => {
    async function importantfunction1 (){
        // things
        resolve();
    }
});

function irrelivantfunction2 (){
    // things
}

var promise2 = new Promise ((resolve, reject) => {
    async function importantfunction1 (){
        // things
    resolve()   
    }
});

One for each asynchronous function. And, resolved at the end like this:

Promise.all([promise1, promise2]).then(() => setInterval (GameLoop, time));

Also, add '() =>' to the set interval or it will run immediately.

Colin Smith
  • 300
  • 2
  • 8