문제

I'm playing around trying to learn Node+Express and Angular by writing a recipe application.

I'm storing an array of recipes and an array of ingredients in a json file as my data store and I have a simple saveMeal() and saveIngredient() method in Node like this:

exports.saveIngredient = function(req, res) {

    var ing = req.body;
    console.log('Saving Ingredient: ' + JSON.stringify(ing, null, 4));

    fs.readFile('./data/data.json', 'utf-8', function (error, data) {

        var mData = JSON.parse(data);

        ing.id = generateId(ing.name, mData.ingredients);
        mData.ingredients.push(ing);

        fs.writeFile('./data/data.json', JSON.stringify(mData, null, 4), function(err) {
            res.writeHead(200, { 'Content-Type': 'text/json' });
            res.end(ing.id, 'utf-8');
        });
    }); 
};

The problem I'm having is that the Angular app fires off a series of calls back to Node to save each ingredient without waiting for each one to finish before firing off the next call and although each call returns an id for the ingredient, only the last ingredient saves, so the last call must be overwriting the data before it.

I know I could change the way the Angular app works to wait for a response before saving the next ingredient, but I kinda feel the Node app should be able to support requests like this. Is there a way to lock the file or a different approach I should be taking?

도움이 되었습니까?

해결책

The problem is that you are running multiple requests to save the file at the same time.

Save A arrives and async loads the file - during which time Save B arrives and loads before Save A has committed its change.

The solution is to run only one of these save requests at a time - something like this (which I've not tested) that keeps an array of save functions and chunks through them in sequence of arriving:

// an array of functions in the queue
var fnBuffer = []

// are we currently running a function in the queue
var running = false

// run the queue one at a time - on finish run again
function runSave(){
    if(running) return
    running = true
    var nextfn = fnBuffer.shift()
    if(!nextfn) return
    nextfn(function(){
        running = false
        runSave()
    })
}

exports.saveIngredient = function(req, res) {

    // push the function onto the queue
    fnBuffer.push(function(callback){

        // this is the original code - the only extra is the callback
        var ing = req.body;
        console.log('Saving Ingredient: ' + JSON.stringify(ing, null, 4));

        fs.readFile('./data/data.json', 'utf-8', function (error, data) {

            var mData = JSON.parse(data);

            ing.id = generateId(ing.name, mData.ingredients);
            mData.ingredients.push(ing);

            fs.writeFile('./data/data.json', JSON.stringify(mData, null, 4), function(err) {
                res.writeHead(200, { 'Content-Type': 'text/json' });
                res.end(ing.id, 'utf-8');

                // running this callback tells the queue to run the next function
                callback()
            });
        }); 
    })

    // trigger the queue
    runSave()
};
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top