是否可以编写asynchronous Node.js代码;清洁剂;?
在Node.js中编写代码时,我遇到了很多情况,因为很难实现一些与数据库查询(I/O)混合的复杂逻辑 考虑一个用python编写的示例。我们需要迭代一个值数组,对于查询数据库的每个值,然后根据结果计算平均值是否可以编写asynchronous Node.js代码;清洁剂;?,node.js,asynchronous,Node.js,Asynchronous,在Node.js中编写代码时,我遇到了很多情况,因为很难实现一些与数据库查询(I/O)混合的复杂逻辑 考虑一个用python编写的示例。我们需要迭代一个值数组,对于查询数据库的每个值,然后根据结果计算平均值 def foo: a = [1, 2, 3, 4, 5] result = 0 for i in a: record = find_from_db(i) # I/O operation if not record: raise Error('No re
def foo:
a = [1, 2, 3, 4, 5]
result = 0
for i in a:
record = find_from_db(i) # I/O operation
if not record:
raise Error('No record exist for %d' % i)
result += record.value
return result / len(a)
Node.js中的相同任务
function foo(callback) {
var a = [1, 2, 3, 4, 5];
var result = 0;
var itemProcessed = 0;
var error;
function final() {
if (itemProcessed == a.length) {
if (error) {
callback(error);
} else {
callback(null, result / a.length);
}
}
}
a.forEach(function(i) {
// I/O operation
findFromDb(function(err, record) {
itemProcessed++;
if (err) {
error = err;
} else if (!record) {
error = 'No record exist for ' + i;
} else {
result += record.value;
}
final();
});
});
}
您可以看到,这样的代码更难写/读,而且更容易出错。
我的问题是:
回答您的问题:
async
在这里也有帮助,因为它减少了您可能会经常重复的此类样板文件async
目前没有真正允许跳过单个任务的方法,但是如果您正在编写样板文件,您可以自己轻松地完成这项工作(例如,只需将函数索引变量增加2)async
,您将发现使用node进行开发更容易。在节点中编写异步vs同步通常会更加复杂(尽管与回调/承诺相比,生成器、光纤等的编写要简单得多)async.parallel ({
"job1": loadFromCollection1,
"job2": loadFromCollection2,
},
function (initError, results) {
if (initError) {
console.log ("[INIT] Server initialization error occurred: " + JSON.stringify(initError, null, 3));
return callback (initError);
}
// Do more stuff with the results
});
事实上,可以遵循相同的方法,并且可以将不同的参数传递给对应于不同作业的不同函数;例如,见
老实说,我更喜欢非阻塞的节点方式。我认为node迫使人们有一个更好的设计,有时您会花时间创建更多的定义,并在数组中对函数和对象进行分组,以便编写更好的代码。我认为原因是,最终您希望利用async
的一些变体,并相应地混合和合并内容。在我看来,如果您还考虑到节点是异步的,那么花一些额外的时间和多花一点时间思考代码是值得的
除此之外,我认为这是一种习惯。为节点编写代码的人越多,就越能改进并编写更好的异步代码。node的好处在于,它确实迫使人们编写更健壮的代码,因为人们开始更加尊重所有函数中的所有错误代码。例如,人们多久检查一次,比如说malloc
或new
是否成功,并且在发出命令后没有NULL
指针的错误处理程序?不过,编写异步代码会迫使人们尊重事件和事件所包含的错误代码。我想一个明显的原因是,我们尊重自己编写的代码,最终我们必须编写返回错误的代码,以便调用方知道发生了什么
我真的认为您需要给它更多的时间,并开始更多地使用异步。就这些。这里的每个人似乎都在建议,这是一个很棒的图书馆。但要给出另一个建议,您应该看看,这是一个新的内置语言(目前有几个非常好的多边形填充)。它允许您以更结构化的方式编写异步代码。例如,请查看以下代码:
var items = [ 1, 2, 3, 4 ];
var processItem = function(item, callback) {
// do something async ...
};
var values = [ ];
items.forEach(function(item) {
processItem(item, function(err, value) {
if (err) {
// something went wrong
}
values.push(value);
// all of the items have been processed, move on
if (values.length === items.length) {
doSomethingWithValues(values, function(err) {
if (err) {
// something went wrong
}
// and we're done
});
}
});
});
function doSomethingWithValues(values, callback) {
// do something async ...
}
使用承诺,它将写下如下内容:
var items = [ 1, 2, 3, 4 ];
var processItem = function(item) {
return new Promise(function(resolve, reject) {
// do something async ...
});
};
var doSomethingWithValues = function(values) {
return new Promise(function(resolve, reject) {
// do something async ...
});
};
// promise.all returns a new promise that will resolve when all of the promises passed to it have resolved
Promise.all(items.map(processItem))
.then(doSomethingWithValues)
.then(function() {
// and we're done
})
.catch(function(err) {
// something went wrong
});
第二个版本更干净、更简单,几乎没有触及真正权力的表面。而且,正如我所说,承诺作为一种新的内置语言出现在es6中,因此(最终)您甚至不需要在库中加载,它将是可用的。只需编写更简洁的代码:
// parallel version
function foo (cb) {
var items = [ 1, 2, 3, 4, 5 ];
var pending = items.length;
var result = 0;
items.forEach(function (item) {
findFromDb(item, function (err, record) {
if (err) return cb(err);
if (!record) return cb(new Error('No record for: ' + item))
result += record.value / items.length;
if (-- pending === 0) cb(null, result);
});
});
}
与您发布的针对python的9行sloc相比,它的源代码行数为13行。但是,与您发布的python不同,此代码运行p中的所有作业
// sequential version
function foo (cb) {
var items = [ 1, 2, 3, 4, 5 ];
var len = items.length;
var result = 0;
(function next () {
if (items.length === 0) return cb(null, result);
var item = items.shift();
findFromDb(item, function (err, record) {
if (err) return cb(err);
if (!record) return cb(new Error('No record for: ' + item))
result += record.value / len;
next();
});
})();
}
var db = require("some-db-abstraction");
function handleWithdrawal(req,res){
try {
var amount=req.param("amount");
db.select("* from sessions where session_id=?",req.param("session_id"),function(err,sessiondata) {
if (err) throw err;
db.select("* from accounts where user_id=?",sessiondata.user_ID),function(err,accountdata) {
if (err) throw err;
if (accountdata.balance < amount) throw new Error('insufficient funds');
db.execute("withdrawal(?,?),accountdata.ID,req.param("amount"), function(err,data) {
if (err) throw err;
res.write("withdrawal OK, amount: "+ req.param("amount"));
db.select("balance from accounts where account_id=?", accountdata.ID,function(err,balance) {
if (err) throw err;
res.end("your current balance is " + balance.amount);
});
});
});
});
}
catch(err) {
res.end("Withdrawal error: " + err.message);
}
var db = require("some-db-abstraction"), wait=require('wait.for');
function handleWithdrawal(req,res){
try {
var amount=req.param("amount");
sessiondata = wait.forMethod(db,"select","* from session where session_id=?",req.param("session_id"));
accountdata= wait.forMethod(db,"select","* from accounts where user_id=?",sessiondata.user_ID);
if (accountdata.balance < amount) throw new Error('insufficient funds');
wait.forMethod(db,"execute","withdrawal(?,?)",accountdata.ID,req.param("amount"));
res.write("withdrawal OK, amount: "+ req.param("amount"));
balance=wait.forMethod(db,"select","balance from accounts where account_id=?", accountdata.ID);
res.end("your current balance is " + balance.amount);
}
catch(err) {
res.end("Withdrawal error: " + err.message);
}
var wait = require('wait.for');
//run in a Fiber
function process() {
var a = [1, 2, 3, 4, 5];
var result = 0;
a.forEach(function(i) {
// I/O operation
var record = wait.for(findFromDb,i); //call & wait for async function findFromDb(i,callback)
if (!record) throw new Error('No record exist for ' + i);
result += record.value;
});
return result/a.length;
}
function inAFiber(){
console.log('result is: ',process());
}
// run the loop in a Fiber (keep node spinning)
wait.launchFiber(inAFiber);