Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I emulate "sleep" in NodeJS?

I am building a browser game with a mini map of the surroundings of the player. I need to track where other players are and update this mini map whenever someone moves. I am implementing this in NodeJS and CouchDB. My design goes as follows:

I have a database for all changing game data. In this database, I have a document that contains the basic map data in a two-dimensional array, with each square on the grid being represented by an element in this array. Since I could have tons of different users all moving about the map simultaneously, I needed some way to ensure that I don't get a read as someone else is writing to it (I could get faulty information if a ton of users are reading and writing to a single document). I decided to have separate documents in this database which represent the individual squares, and each document has the players that are "on" that square and some other data associated with that square. Essentially, the map document is used only as a lookup table for the square document. This enables me to change a single document without having to rewrite the entire map document, solving the problem of simultaneous reads and writes.

My problem, though, is that I need to get a mini map for the user to use as a reference. This mini map will have the documents of the surrounding squares. Since I need all of this at the same time, I thought I would just grab all 9 squares from the database and return it in a single ajax response. My problem though is with reducing the amount of blocking IO that I do. Right now, I have a nested loop that requests the squares I need from the database. Here's a look at my code (position and map are passed in):

var miniMap = new Array();
var keyMap = new Object();
var numDone = 0;
for(i = position.y - 1, y = 0; i < position.y + 1 && i < map.length; i++, y++){
    miniMap[i] = new Array();
    for(v = position.x - 1, x = 0; v < position.x + 1 && v < map.length; v++, x++){
        keyMap[map[i][v].id] = {'x': x, 'y': y};
        gameDB.getDoc(map[i][v].id, function(er, doc){
            var tPos = keyMap[doc._id];
            miniMap[tPos.y][tPos.x] = doc;
            numDone++;
        });
    }
}

My problem, though, is that getDoc is non-blocking, so I don't know when it will set the square data to miniMap. I thought about doing something like this:

while(numDone < 9){
    sleep(10);
}
callback(miniMap);

This will let me wait until CouchDB is finished getting all of my data, but JavaScript doesn't have a sleep function. The closest I found was setTimeout, but that is also non-blocking and I'll never be 100% sure that the time I chose for the timeout will be sufficient to allow CouchDB to finish getting my data.

So basically I want to have a condition, test it, then return it to the event stack again if the condition is false. The only solution I thought of was to have a recursive setTimeout callback that would do something like this:

function testCallback(data, condition, callback){
    if(data < condition){
        setTimeout(function(){
            testCallback(data, condition, callback);
        }, 10);
    }else{
        callback(data);
    }
}

This seems pretty terrible... Is there a better way that I could be doing this? Should I give up this approach and force there to be multiple ajax calls to get updated data? Should I go for a blocking approach?

like image 236
beatgammit Avatar asked Dec 20 '10 07:12

beatgammit


People also ask

How do I introduce sleep in node JS?

Using the Sleep Command You can use execSync to invoke your OS' sleep command. const {execSync} = require('child_process'); execSync('sleep 1'); // block process for 1 second. async function run() { const start = Date. now(); await Promise.

Is there a sleep function in JS?

Unlike Java or Python, Javascript does not have a built-in sleep function. So, how might you be able to implement a method with the same functionality as the sleep function? A simple way to create a delay in Javascript is to use the setTimeout method.

Can you use setTimeout in node?

The setTimeout function is used to call a function after the specified number of milliseconds. The delay of the called function begins after the remaining statements in the script have finished executing. The setTimeout function is found in the Timers module of Node.


1 Answers

Just use another callback:

function getMiniMap(....., callback) { // supply the callback that sends the data
    var miniMap = new Array();
    var keyMap = new Object();
    var numDone = 0;
    ...
                numDone++;
                if (numDone === 9) { // as soon as everything has been collected...
                    callback(miniMap); // ... call the send callback and supply it the miniMap
                }
            });
        }
    }
}

Oh, and your Database Model is really bad, I don't know much about your game, but unless this needs to run on multiple Node processes, it would be better to keep the Map etc. in a JS Array and only write to the DB when the server needs to save state.

Oh and you can also replace your keyMap with an anonymous function call:

    (function(x, y) {
        gameDB.getDoc(map[i][v].id, function(er, doc){
            var tPos = keyMap[doc._id];
            miniMap[tPos.y][tPos.x] = doc;
            numDone++;
        });
    })(x, y); // pass in a copy of x and y
like image 133
Ivo Wetzel Avatar answered Sep 19 '22 12:09

Ivo Wetzel