我正在尝试编写一个Node程序,用我在磁盘上的文件中的数据填充我的MySQL数据库。我可能会或可能不会以正确的方式解决这个问题,但它正在发挥作用。我遇到的问题是理解我应该如何处理,以便在结束与DB的连接之前完成异步功能。最后,我将阅读大量数据文件,并将其插入数据库,就像我在下面所做的那样。我可以使用readFileSync
而不是异步版本,但我需要更好地处理异步函数。
当我在下面插入葡萄酒类别时,它工作正常,因为它没有使用异步函数。但是,当我使用readFile
从文件中获取数据时,我会在执行任何查询之前收到连接结束的错误:
connection.connect( function(err) {
if(err) {
console.log(err);
}
});
// Take a table and the values, and insert a new row into a table
function insert_into( table, values ) {
if( values instanceof Array ) {
values = values.map( function( value ) {
return '"' + value + '"';
}).join(', ');
} else {
values = '"' + values + '"';
}
var statement = 'INSERT INTO ' + table + ' VALUES (NULL, ' + values + ')';
connection.query( statement, function(err, rows, fields) {
if (err) throw err;
console.log( values + " successfully added.");
});
};
// Populate the wine_categories table
var wine_categories = [
'red', 'white', 'rose', 'sparkling', 'fortified'
];
// Works fine when used alone
wine_categories.forEach( function( element ) {
insert_into( 'wine_categories', element );
});
// Populate the countries table
// connection.end() runs before this finishes its job
fs.readFile( countries, 'utf8', function (err, data) {
if (err) {
throw err;
} else {
var codes = Array.prototype.map.call(
data.split('\n'), function( country ) {
return country.split('\t');
});
codes.forEach( function( country ) {
if( country[1].length > 25 ) {
country[1] = country[1].substring(0, 25);
}
insert_into( 'countries', country );
});
}
});
connection.end();
显然,connection.end()
需要在所有插入完成后发生,但我不知道如何处理。我不希望它成为readFile
调用的回调,因为我最终会在此文件中有许多类似的调用。
我应该如何构建我的代码,以便所有查询都执行,connection.end()
在完成后运行?对于异步的wiz来说,答案可能是显而易见的......
答案 0 :(得分:2)
使用promises就像这样:
pool.getConnectionAsync().then(function(connection) {
// Populate the wine_categories table
var wine_categories = [
'red', 'white', 'rose', 'sparkling', 'fortified'
];
var wineQueries = wine_categories.map(function(wine){
return insert_into(connection, "wine_categories", wine);
});
var countryQueries = fs.readFileAsync(countries, "utf-8").then(function(data) {
return data.split("\n").map(function(country) {
country = country.split("\t")[1];
if (country.length > 25) {
country = country.substring(0, 25);
}
return insert_into(connection, "countries", country);
});
});
Promise.all(wineQueries.concat(countryQueries))
.then(function() {
console.log("all done");
})
.catch(function(e) {
console.log("error", e);
})
.finally(function() {
connection.release();
})
});
上述
的先决条件代码var Promise = require("bluebird");
var fs = Promise.promisifyAll(require("fs"));
Promise.promisifyAll(require("mysql/lib/Connection").prototype);
var pool = Promise.promisifyAll(require("mysql").createPool({
"user": "...",
"password": "...",
"database": "...",
"host": "localhost",
"port": 3306,
"debug": false
}));
function insert_into(connection, table, values) {
if( values instanceof Array ) {
values = values.map(connection.escape, connection).join(', ');
} else {
values = connection.escape(values);
}
return connection
.queryAsync('INSERT INTO ' + table + ' VALUES (NULL, ' + values + ')')
.then(function() {
console.log(values + " successfully added.");
});
}
答案 1 :(得分:1)
假设insert_into
也是异步的,您可能希望使用async.each
之类的东西来处理插入记录。它有一个方便的回调函数,当插入所有记录时将调用它,因为只有在那一点上你想要关闭连接:
async.each(codes, function(country, callback) {
if ( country[1].length > 25 ) {
country[1] = country[1].substring(0, 25);
}
insert_into( 'countries', country, callback ); // !! read below
}, function(err) {
// TODO: handle any errors
...
// Here, all countries are inserted.
connection.end();
});
但是,这意味着还应该使insert_into
接受在插入记录时将调用的回调(使用公共节点约定function(err, result)
)。在上面的代码中,我直接使用async
提供的回调,这意味着一旦你的insert_into
完成,它将调用async
回调信号,表明这次{{1}的迭代完成了。
编辑:您可以重写each
,看起来像这样:
insert_into
由于您不需要function insert_into( table, values, callback ) {
...
connection.query(..., function(err) {
callback(err);
});
}
的实际结果,您只需要传递connection.query
(而不是投掷它)。
提示:假设您使用的是node-mysql
,您可能需要查看有关如何使用escaping帮助您的文档。