2015-06-27 1 views
1

У меня есть таблица, где люди сохраняют свое время (в секундах), где они были активны. Я хотел бы написать функцию, которая собирает общее время в другой таблице с именем gather.Результаты сбора синхронных вызовов node.js

Для каждой строки я проверяю, существует ли запись в таблице gather. В зависимости от этого результата я делаю вставку или обновление.

db.serialize(function() { 
    db.each("SELECT * from TEST", function(err, row) { 
     db.get("SELECT * from GATHER where name = " + row.name "", function(err, row) { 
      if(row === undefined || row === null){ 
       var stmt = db.prepare("INSTER INTO gather (name, time) VALUE(?,?)"); 
       stmt.run([name, seconds], function(error){ 
        console.log('lastInsert ' + this.lastID); 
       }); 
       stmt.finalize(); 
      }else{ 
       seconds += row.time;//increment time 
       var stmt = db.prepare("UPDATE gather SET time = ? WHERE name = ?"); 
       stmt.run([seconds, row.name], function(error){ 
        console.log('lastInsert ' + row.idProcessed); 
       }); 
       stmt.finalize(); 
      } 
     }); 
    }); 
}); 

Проблема, заключающаяся в том, что sqlite работает асинхронно. Поэтому в моей таблице gather создано несколько записей, хотя строки должны быть обновлены.

Что было бы правильным способом запустить эту функцию синхронно? Должен ли я ограничивать линии и вызывать функцию каждую секунду или есть умный способ?

ответ

2

Вы можете использовать async. Например (но сначала вы должны прочитать заключительные замечания в конце):

var async = require('async'); 

var data = {} 

var yourFirstSelect() = function(callback){ 
    //You do your first select 
    //... 
    db.get("SELECT * from TEST", function(err, row) { 
    if(row){ 
     data.name = row.name; 
     data.otherInterestingAttribute = row.otherInterestingAttribute; 
     callback(err, data); 
    }else{ 
     callback('Row is null'); 
    } 
    }) 
    //.. 
} 

var yourSecondSelect() = function(data, callback){ 
    //You do your second select 
    //... 
    db.get("SELECT * from GATHER where name = " + data.name "", function(err, row) { //Note that I'm using data instead of row 
    if(row){ 
     data.seconds = row.seconds; 
     data.otherInterestingAttribute = row.otherInterestingAttribute; 
     callback(err, data); 
    }else{ 
     callback('Row is null'); 
    } 
    }) 
    //.. 
} 

var decide() = function(data, callback){ 
    if (data.somethingExists) { //Do your validations 
    data.type = 'update'; 
    callback(err, data); 
    } else { 
    data.type = 'insert'; 
    callback(err, data); 
    } 
} 

var update() = function(data,callback){ 
    if (data.type === 'update') { 
    //... 
    //Do your stuff in order to update 
    seconds += row.time;//increment time 
     var stmt = db.prepare("UPDATE gather SET time = ? WHERE name = ?"); 
      stmt.run([seconds, row.name], function(error){ 
      console.log('update ' + row.idProcessed); 
      }); 
     stmt.finalize(); 
    //... 
    } else { 
    callback(err,data); 
    } 
} 

var insert() = function(data,callback){ 
    if (data.type === 'insert') { 
    //... 
    //Do your stuff in order to insert 
    var stmt = db.prepare("INSTER INTO gather (name, time) VALUE(?,?)"); 
     stmt.run([data.name, data.seconds], function(error){ 
      console.log('lastInsert ' + this.lastID); 
      callback(err,data); 
     }); 
     stmt.finalize(); 
    //... 
    } else { 
    callback(err,data); 
    } 
} 

var niceWorkflow = function(){ 
    async.waterfall([ 
     yourFirstSelect, 
     yourSecondSelect, 
     decide, 
     update, 
     insert 
    ], 
    function (err, result) { 
    console.log('Done'); 
    }) 
} 

//and call your workflow 
niceWorkflow(); 

Конечно же, это не рабочий код 100%, я написал его, чтобы посмотреть еще один способ сделать то, что вы пытаетесь. Многие переменные, валидации и т. Д. Являются лишь примерами, и я намеренно забыл db.each, чтобы не быть слишком расширенным и запутывающим вас и пытаться ответить на ваш последний вопрос Is there a smarter way?.

+0

Semms как умный способ! Проблема все же остается прежней. Db.each будет выдавать сразу несколько строк, и я бы хотел сказать, что он явно обрабатывает один за другим, потому что каждая строка зависит от результата раньше. – Silve2611

+0

@ Silve2611 Привет, вы решили свою проблему? Я не комментировал здесь раньше, потому что был очень шумным, но вы можете использовать everySeries для выполнения цикла for по результатам массива первого запроса. – Gepser

+0

Я нашел решение со временем, которое работает для меня, но я не уверен, что он самый умный. Я не уверен, что могу применить каждую серию здесь. db.get возвращает только первую строку. db.each не вернет массив, но даст мне обратный вызов, который будет работать асинхронно. Вы видите более разумное решение? Я мог бы также опубликовать мой. – Silve2611

0

Вы также можете использовать Q.all от Q library. Он вернет обещание после того, как все обещания будут решены. Это одно обещание терпит неудачу, тогда Q.all будет отвергнут.

+0

Не могли бы вы объяснить, как это можно сделать с помощью db.each? Я нахожу подход очень интересным, и я прочитал обещания. Я попробую, но все же я не знаю, как я могу сказать, что db.each выполняет одну строку за другой. – Silve2611

+0

Какую библиотеку вы используете, любой ORM? Я имею в виду, что такое db? вы проверили [Sequelize] (http://sequelize.readthedocs.org/en/latest/) –

+0

Если db.get уже возвращает обещание, вам просто нужно создать массив ** db.get ** s : –