[英]Avoiding callback hell in nodeJs / Passing variables to inner functions
這是我想簡化的一個例子:
exports.generateUrl = function (req, res) {
var id = req.query.someParameter;
var query = MyMongooseModel.findOne({'id': id});
query.exec(function (err, mongooseModel) {
if(err) {
//deal with it
}
if (!mongooseModel) {
generateUrl(Id,
function (err, text, url) {
if (err) {
res.status(HttpStatus.INTERNAL_SERVER_ERROR).send(err);
return;
}
var newMongooseModel = new AnotherMongooseModel();
newMongooseModel.id = id;
newMongooseModel.save(function (err) {
if (err) {
res.status(HttpStatus.INTERNAL_SERVER_ERROR).send(err);
} else {
res.send({url: url, text: text});
}
});
});
} else {
//deal with already exists
}
});
};
我已經看到了其他SO的答案,他們告訴你使用命名函數,但是沒有說明如何處理你想要傳入的變量或者使用jQuery的隊列。 我也沒有奢侈品。
我知道我可以用名稱函數替換我的匿名函數,但是我需要傳遞變量。 如果函數在別處定義,我的內部函數如何訪問res
?
你問題的核心是:
我知道我可以用名稱函數替換我的匿名函數,但是我需要傳遞變量。 如果函數在別處定義,我的內部函數如何訪問res?
答案是使用功能工廠。
一般來說,這個:
function x (a) {
do_something(function(){
process(a);
});
}
可以轉換為:
function x (a) {
do_something(y_maker(a)); // notice we're calling y_maker,
// not passing it in as callback
}
function y_maker (b) {
return function () {
process(b);
};
}
在上面的代碼中, y_maker
是一個生成函數的函數(讓我們調用該函數的目的“y”)。 在我自己的代碼中,我使用命名約定.._maker
或generate_..
來表示我正在調用函數工廠。 但這只是我而且這個慣例絕不是標准的,也不是廣泛采用的。
因此,對於您的代碼,您可以將其重構為:
exports.generateUrl = function (req, res) {
var id = req.query.someParameter;
var query = MyMongooseModel.findOne({'id': id});
query.exec(make_queryHandler(req,res));
};
function make_queryHandler (req, res) {
return function (err, mongooseModel) {
if(err) {
//deal with it
}
else if (!mongooseModel) {
generateUrl(Id,make_urlGeneratorHandler(req,res));
} else {
//deal with already exists
}
}}
function make_urlGeneratorHandler (req, res) {
return function (err, text, url) {
if (err) {
res.status(HttpStatus.INTERNAL_SERVER_ERROR).send(err);
return;
}
var newMongooseModel = new AnotherMongooseModel();
newMongooseModel.id = id;
newMongooseModel.save(make_modelSaveHandler(req,res));
}}
function make_modelSaveHandler (req, res) {
return function (err) {
if (err) res.status(HttpStatus.INTERNAL_SERVER_ERROR).send(err);
else res.send({url: url, text: text});
}}
這使得嵌套回調變得扁平化。 作為額外的好處,您可以正確命名函數應該執行的操作。 我認為這是好的做法。
它還有一個額外的好處,它比使用匿名回調(使用嵌套回調或使用promises)快得多,但如果你將命名函數傳遞給promise.then()而不是匿名函數,那么你將獲得相同的加速福利)。 之前的SO問題(我的google-fu今天讓我失敗了)發現命名函數的速度是node.js中匿名函數的兩倍以上(如果我沒記錯的話,速度提高了5倍多)。
使用承諾。 使用Q和mongoose-q它會給出:類似的東西:
exports.generateUrl = function (req, res) {
var id = req.query.someParameter;
var text = "";
var query = MyMongooseModel.findOne({'id': id});
query.execQ().then(function (mongooseModel) {
if (!mongooseModel) {
return generateUrl(Id)
}).then(function (text) {
var newMongooseModel = new AnotherMongooseModel();
newMongooseModel.id = id;
text = text;
newMongooseModel.saveQ()
}).then(function (url) {
res.send({url: url, text: text});
}).fail(function(err) {
res.status(HttpStatus.INTERNAL_SERVER_ERROR).send(err);
});
};
命名函數將在匿名函數所在的范圍內執行,並且可以訪問您當前使用的所有變量。 這種方法可以使你的代碼更少嵌套,更易讀(這很好),但技術上仍然是“回調地獄”。 避免這種情況的最好方法是使用像Q這樣的promise庫來包裝異步庫(假設它們還沒有提供promises)。 IMO,承諾提供了更加清晰的執行路徑圖。
通過使用bind
參數綁定到命名函數,可以避免不知道變量來自何處的困境,例如:
function handleRequest(res, err, text, url) {
if (err) {
res.status(HttpStatus.INTERNAL_SERVER_ERROR).send(err);
return;
}
var newMongooseModel = new AnotherMongooseModel();
newMongooseModel.id = id;
newMongooseModel.save(function (err) {
if (err) {
res.status(HttpStatus.INTERNAL_SERVER_ERROR).send(err);
} else {
res.send({url: url, text: text});
}
});
}
...
generateUrl(Id, handleRequest.bind(null, res));
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.