在下面的代码我是在callbackhell?如何在纯javascript中不使用任何异步模块的情况下克服这种情况?
emailCallBack(e_data, email);
if (email_list.length) {
checkEmail(email_list.pop());
} else {
completionCallback();
}
Run Code Online (Sandbox Code Playgroud)
上面的代码被复制到多个位置,以使代码按预期工作.
function processInviteEmails(email_list, user_id, emailCallBack, completionCallback){
function checkEmail(email){
try {
check(email).isEmail();
//is valid email
checkConnected(email, user_id, function(connect_status, user_row, user_meta_row, connect_row){
var e_data;
//insert to connect and send msg to queue
if(connect_status === 'not connected'){
var cur_date = moment().format('YYYY-MM-DD');
var dbData = {
"first_name": '',
"last_name": '',
"email": email,
"user_id": user_id,
"status": "invited",
"unsubscribe_token": crypto.randomBytes(6).toString('base64'),
"created": cur_date,
"modified": cur_date
};
ConnectModel.insert(dbData, function(result){
if (result.insertId > 0) {
//send to email queue
//Queue Email
MailTemplateModel.getTemplateData('invitation', function(res_data){
if(res_data.status === 'success'){
var unsubscribe_hash = crypto.createHash("md5")
.update(dbData.unsubscribe_token + email)
.digest('hex');
var unsubscribe_link = app.locals.SITE_URL+'/unsubscribe/' + result.insertId + '/' + unsubscribe_hash;
var template_row = res_data.template_row;
var user_full_name = user_row.user_firstname+' '+ user_row.user_lastname;
var invitation_link = 'http://'+user_row.url_alias+'.'+ app.locals.SITE_DOMAIN;
var mailOptions = {
"type": 'invitation',
"to": dbData.email,
"from_name" : user_full_name,
"subject": template_row.message_subject
.replace('[[USER]]', user_full_name),
"text": template_row.message_text_body
.replace('[[USER]]', user_full_name)
.replace('[[INVITATION_LINK]]', invitation_link)
.replace('[[UNSUBSCRIBE_LINK]]', unsubscribe_link),
"html": template_row.message_body
.replace('[[USER]]', user_full_name)
.replace('[[INVITATION_LINK]]', invitation_link)
.replace('[[UNSUBSCRIBE_LINK]]', unsubscribe_link)
};
mailOptions = JSON.stringify(mailOptions);
//send email to queue
sqsHelper.addToQueue(cfg.sqs_invitation_url, mailOptions, function(data){
if(data){
e_data = null;
}
else{
e_data = new Error('Unable to Queue ');
}
emailCallBack(e_data, email);
if (email_list.length) {
checkEmail(email_list.pop());
} else {
completionCallback();
}
});
}
else{
e_data = new Error('Unable to get email template');
emailCallBack(e_data, email);
if (email_list.length) {
checkEmail(email_list.pop());
} else {
completionCallback();
}
}
});
}
else{
e_data = new Error('Unable to Insert connect');
emailCallBack(e_data, email);
if (email_list.length) {
checkEmail(email_list.pop());
} else {
completionCallback();
}
}
});
}
else{
e_data = new Error('Already connected');
emailCallBack(e_data, email);
if (email_list.length) {
checkEmail(email_list.pop());
} else {
completionCallback();
}
}
});
} catch (e) {
//invalid email
emailCallBack(e, email);
if (email_list.length) {
checkEmail(email_list.pop());
} else {
completionCallback();
}
}
}
checkEmail(email_list.pop());
}
Run Code Online (Sandbox Code Playgroud)
Pet*_*ons 31
是的,你在回调地狱.假设您不想使用异步的解决方案(我怀疑您可以证明除偏见以外的其他方式)包括:
1)制作更多顶级功能.根据经验,每个函数应执行1或2个IO操作.
2)调用这些函数,使代码遵循由一小组控制流"粘合"函数组织成业务逻辑的一长串短核函数模式.
代替:
saveDb1 //lots of code
saveDb2 //lots of code
sendEmail //lots of code
Run Code Online (Sandbox Code Playgroud)
目标:
function saveDb1(arg1, arg2, callback) {//top-level code}
function saveDb2(arg1, arg2, callback) {//top-level code}
function sendEmail(arg1, arg2, callback) {//top-level code}
function businessLogic(){//uses the above to get the work done}
Run Code Online (Sandbox Code Playgroud)
3)使用更多的函数参数,而不是依赖于闭包
4)发出事件并解除你的代码!了解如何嵌套代码将数据写入数据库,然后构建电子邮件并将其添加到队列中?难道你不明白这两者是不是一个接一个地存在的?电子邮件非常适合发送事件的核心业务逻辑和收听这些事件并对邮件进行排队的电子邮件模块.
5)从特定事务业务逻辑中分离应用程序级服务连接代码.处理与网络服务的连接应该更广泛地处理,而不是嵌入一组特定的业务逻辑.
6)阅读其他模块以获取示例
至于你应该使用异步库,你可以而且应该弥补自己的心态有关,但后你知道了,知道得很好,这些方法的每一项:
任何严肃的node.js开发人员都知道如何在所有这些范例中使用和工作.是的,每个人都有他们喜欢的方法,也许有一些书呆子愤怒的关于非偏爱的方法,但这些都不困难,如果没有能够指出你从头开始编写的一些非平凡的代码,那么你的决定就不好了.每个范例.此外,您应该尝试几个帮助程序库,并了解它们的工作原理以及它们为什么要为您节省样板.研究Tim Caswell Step或Caolan McMahon的工作async将会非常具有启发性.您是否看过everyauth源代码对promises的使用?我个人不喜欢它,但我肯定不得不承认作者已经在该库的每一个重复点附近挤压了,并且他使用promises的方式将把你的大脑变成椒盐卷饼.这些人都是巫师,教授很多.不要仅仅因为时髦点或其他什么而嘲笑这些图书馆.
另外一个好的外部资源是callbackhell.com.
"如果你尝试使用纯node.js编写业务db登录,你就直接回调地狱"
我最近创建了一个名为WaitFor的简单抽象来在同步模式下调用异步函数(基于Fibers):https://github.com/luciotato/waitfor
检查数据库示例:
pure node.js(温和的回调地狱):
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);
}
Run Code Online (Sandbox Code Playgroud)
注意:上面的代码,虽然它看起来会捕获异常,但它不会.使用回调地狱捕捉异常会增加很多痛苦,而且我不确定你是否会有'res'参数来响应用户.如果有人喜欢修复这个例子......请成为我的客人.
使用wait.for:
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);
}
Run Code Online (Sandbox Code Playgroud)
注意:异常将按预期捕获.将使用this = db调用db方法(db.select,db.execute)
为了使用wait.for,你必须标准化你的CALLBACKS才能运行(错误,数据)
如果你标准化你的CALLBACKS,你的代码可能如下所示:
//run in a Fiber
function processInviteEmails(email_list, user_id, emailCallBack, completionCallback){
while (email_list.length) {
var email = email_list.pop();
try {
check(email).isEmail(); //is valid email or throw
var connected_data = wait.for(checkConnected,email,user_id);
if(connected_data.connect_status !== 'not connected') throw new Error('Already connected');
//insert to connect and send msg to queue
var cur_date = moment().format('YYYY-MM-DD');
var dbData = {
"first_name": '',
"last_name": '',
"email": email,
"user_id": user_id,
"status": "invited",
"unsubscribe_token": crypto.randomBytes(6).toString('base64'),
"created": cur_date,
"modified": cur_date
};
result = wait.forMethod(ConnectModel,'insert',dbData);
// ConnectModel.insert shuold have a fn(err,data) as callback, and return something in err if (data.insertId <= 0)
//send to email queue
//Queue Email
res_data = wait.forMethod(MailTemplateModel,'getTemplateData','invitation');
// MailTemplateModel.getTemplateData shuold have a fn(err,data) as callback
// inside getTemplateData, callback with err=new Error('Unable to get email template') if (data.status !== 'success')
var unsubscribe_hash = crypto.createHash("md5")
.update(dbData.unsubscribe_token + email)
.digest('hex');
var unsubscribe_link = app.locals.SITE_URL+'/unsubscribe/' + result.insertId + '/' + unsubscribe_hash;
var template_row = res_data.template_row;
var user_full_name = user_row.user_firstname+' '+ user_row.user_lastname;
var invitation_link = 'http://'+user_row.url_alias+'.'+ app.locals.SITE_DOMAIN;
var mailOptions = {
"type": 'invitation',
"to": dbData.email,
"from_name" : user_full_name,
"subject": template_row.message_subject
.replace('[[USER]]', user_full_name),
"text": template_row.message_text_body
.replace('[[USER]]', user_full_name)
.replace('[[INVITATION_LINK]]', invitation_link)
.replace('[[UNSUBSCRIBE_LINK]]', unsubscribe_link),
"html": template_row.message_body
.replace('[[USER]]', user_full_name)
.replace('[[INVITATION_LINK]]', invitation_link)
.replace('[[UNSUBSCRIBE_LINK]]', unsubscribe_link)
};
mailOptions = JSON.stringify(mailOptions);
//send email to queue ... callback(err,data)
wait.forMethod(sqsHelper,'addToQueue',cfg.sqs_invitation_url, mailOptions);
} catch (e) {
// one of the callback returned err!==null
emailCallBack(e, email);
}
} // loop while length>0
completionCallback();
}
// run the loop in a Fiber (keep node spinning)
wait.launchFiber(processInviteEmails,email_list, user_id, emailCallBack, completionCallback);
Run Code Online (Sandbox Code Playgroud)
看到?没有回调地狱
| 归档时间: |
|
| 查看次数: |
8655 次 |
| 最近记录: |