2016-02-04 6 views
0

Я уверен, что моя проблема напрямую не связана с использованием Koa или SQLite3, но более подходит для общего использования JavaScript.Подождите, пока не будет выполнен запрос БД

У меня есть следующие server.js:

var http = require('http'); 
var koa = require('koa'); 
var route = require('koa-route'); 
var app = koa(); 
var sqlite3 = require('sqlite3').verbose(); 
var dbFile = 'coc.db'; 
var db = new sqlite3.Database(dbFile); 

var members = []; 

var printMembers = function(name){ 

     db.each(query, [name], function(err, row){ 
      members.push(row); 
     }); 

    return members; 

} 


app.use(route.get('/', index)); 


function *index() { 

    this.body = printMembers("name"); 

} 


app.listen(8008); 
console.log('Koa listening on port 8008'); 

При запуске сервера и посетить localhost:8008 в первый раз, я только получаю пустой массив ([]) в качестве ответа. Это содержимое переменной members. Когда я перезагружаюсь, я получаю, как и ожидалось, результаты моего SQLite-запроса.

Причина, вероятно, в том, что мой запрос занимает больше времени, чем выполняется сценарий. Конечно, второй раз members заполнен. Но с результатами запроса в первый раз!
Также var members = [] должен быть объявлен в пределах printMembers.

По-моему, мне нужно что-то, чтобы убедиться, что index() ждет, пока db.each() не будет. Но как я могу это достичь?

ответ

3

Поскольку основная абстракция в Koa 1.x заключается в использовании генераторов для потока управления в маршрутах, вы должны использовать yield, чтобы подождать, пока что-то закончится.

Коа использует https://github.com/tj/co под капотом, поэтому при попытке Interop с библиотеками, которые не построены для Коа, как разработчик Коа вы в основном просто нужно выяснить, как вы можете изменить/завернуть асинхронные библиотеки так, что вы можете yield их внутри вашего маршрута.

Вы можете в основном yield генераторы и обещания.

Наиболее универсальным инструментом для работы является обертывание функции с помощью a Promise. При создании Promise (new Promise(fn)) вы должны передать ему функцию fn, которая принимает две функции в качестве аргументов: resolve и reject.

Вы можете часто находить библиотеки со спецификой, в которых кто-то уже обернул популярную библиотеку таким образом, чтобы вы могли использовать ее функции/методы yield.

Например, https://www.npmjs.com/package/co-sqlite3. (Я понятия не имею, насколько это хорошо, но, к счастью, легко обернуть материал, если он окажется не лучшим)

В качестве альтернативы существуют библиотеки, которые могут использовать модуль обратного вызова (то есть большинство модулей узла) и оберните его, чтобы он дал обещания, которые вы можете yield.

Например, bluebird имеет функцию promisifyAll, которая делает это, хотя, честно говоря, я никогда не использовал bluebird.

// (Untested) 
var Promise = require('bluebird'); 
var sqlite3 = require('sqlite3').verbose(); 
var db = Promise.promisifyAll(new sqlite3.Database(':memory:')); 

function* printMembers(name) { 
    var query = '...'; 
    return yield db.allAsync(query, [name]); 
} 

function* index() { 
    // promisifyAll adds an {fnName}Async version of all the functions 
    // to the module you call it on, so now we can call `allAsync`. 

    var members = yield db.allAsync(query, ['name']); 
    // or 
    var members = yield printMembers('name'); 

    this.body = members; 
} 

Но я дам вам несколько примеров Promise-обертывания, так как это такой важный инструмент, чтобы в вашем комплекте.

Здесь я переписать printMembers функцию, чтобы вернуть обещание, которое вы можете yield от вашего маршрута:

var printMembers = function(name) { 
    var query = '...'; 
    return new Promise(function(resolve, reject) { 
    var rows = []; 
    db.each(query, [name], function(err, row){ 
     if (err) return reject(err); 
     rows.push(row); 
    }); 
    resolve(rows); 
    }); 
} 

Хотя заметим, что sqlite3 имеет Database#all функцию, которая возвращает все строки вам сразу так Вам не придется вручную создать массив:

var printMembers = function(name) { 
    var query = '...'; 
    return new Promise(function(resolve, reject) { 
    db.all(query, [name], function(err, rows){ 
     if (err) return reject(err); 
     resolve(rows); 
    }); 
    }); 
} 

Теперь, вот что ваш маршрут будет выглядеть следующим образом:

function *index() { 
    var members = yield printMembers('name'); 
    this.body = members; 
} 

Если обещание, что попадает reject(err) путь, то обещание положить в отклоненной состоянии и бросит ошибку, которую вы можете попробовать/поймать, если вы хотите, где вы уступаете обещанное.

Если обещание достигло этого пути resolve(members), то это то, что получено и присвоено переменной members.

+0

Вау, это был прекрасный ответ! a) Теперь я знаю, какой lego кирпич JS я отсутствовал, b) вы дали несколько подходов к решению и c) вы даже дали рабочий пример. Спасибо огромное! Его просто сложно, когда у вас есть проблема, но не знаю, что искать! – cvoigt