Kay*_*ale 156 javascript asynchronous functional-programming node.js
我想创建一个显示来自数据库的一些数据的页面,所以我创建了一些从我的数据库中获取数据的函数.我只是Node.js的新手,所以据我所知,如果我想在一个页面中使用所有这些(HTTP响应),我必须将它们全部嵌套:
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/html'});
var html = "<h1>Demo page</h1>";
getSomeDate(client, function(someData) {
html += "<p>"+ someData +"</p>";
getSomeOtherDate(client, function(someOtherData) {
html += "<p>"+ someOtherData +"</p>";
getMoreData(client, function(moreData) {
html += "<p>"+ moreData +"</p>";
res.write(html);
res.end();
});
});
});
Run Code Online (Sandbox Code Playgroud)
如果有很多这样的函数,那么嵌套就成了问题.
有办法避免这种情况吗?我想这与你如何组合多个异步函数有关,这似乎是一个基本的东西.
Dan*_*llo 73
有趣的观察.请注意,在JavaScript中,您通常可以使用命名函数变量替换内联匿名回调函数.
下列:
http.createServer(function (req, res) {
// inline callback function ...
getSomeData(client, function (someData) {
// another inline callback function ...
getMoreData(client, function(moreData) {
// one more inline callback function ...
});
});
// etc ...
});
Run Code Online (Sandbox Code Playgroud)
可以重写看起来像这样:
var moreDataParser = function (moreData) {
// date parsing logic
};
var someDataParser = function (someData) {
// some data parsing logic
getMoreData(client, moreDataParser);
};
var createServerCallback = function (req, res) {
// create server logic
getSomeData(client, someDataParser);
// etc ...
};
http.createServer(createServerCallback);
Run Code Online (Sandbox Code Playgroud)
但是,除非您计划在其他位置重用回调逻辑,否则通常更容易读取内联匿名函数,如您的示例所示.它还可以让您不必为所有回调找到一个名称.
另外请注意,正如@pst在下面的注释中所指出的,如果您正在访问内部函数中的闭包变量,则上述内容不是简单的翻译.在这种情况下,使用内联匿名函数更为可取.
Bag*_*ggz 63
凯,只需使用其中一个模块.
它会变成这样:
dbGet('userIdOf:bobvance', function(userId) {
dbSet('user:' + userId + ':email', 'bobvance@potato.egg', function() {
dbSet('user:' + userId + ':firstName', 'Bob', function() {
dbSet('user:' + userId + ':lastName', 'Vance', function() {
okWeAreDone();
});
});
});
});
Run Code Online (Sandbox Code Playgroud)
进入:
flow.exec(
function() {
dbGet('userIdOf:bobvance', this);
},function(userId) {
dbSet('user:' + userId + ':email', 'bobvance@potato.egg', this.MULTI());
dbSet('user:' + userId + ':firstName', 'Bob', this.MULTI());
dbSet('user:' + userId + ':lastName', 'Vance', this.MULTI());
},function() {
okWeAreDone()
}
);
Run Code Online (Sandbox Code Playgroud)
Cao*_*lan 18
在大多数情况下,我同意Daniel Vassallo.如果你可以将一个复杂且深度嵌套的函数分解为单独的命名函数,那么这通常是一个好主意.对于在单个函数中执行此操作的时间,您可以使用许多可用的node.js异步库之一.人们已经提出了许多不同的方法来解决这个问题,所以请看一下node.js模块页面,看看你的想法.
我自己写了一个模块,名为async.js.使用此,上面的示例可以更新为:
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/html'});
async.series({
someData: async.apply(getSomeDate, client),
someOtherData: async.apply(getSomeOtherDate, client),
moreData: async.apply(getMoreData, client)
},
function (err, results) {
var html = "<h1>Demo page</h1>";
html += "<p>" + results.someData + "</p>";
html += "<p>" + results.someOtherData + "</p>";
html += "<p>" + results.moreData + "</p>";
res.write(html);
res.end();
});
});
Run Code Online (Sandbox Code Playgroud)
这种方法的一个好处是,您可以通过将"系列"功能更改为"并行"来快速更改代码以并行获取数据.更重要的是,async.js也可以在浏览器中运行,因此如果您遇到任何棘手的异步代码,您可以使用与node.js相同的方法.
希望这很有用!
Gui*_*ido 18
您可以将此技巧用于数组而不是嵌套函数或模块.
眼睛容易得多.
var fs = require("fs");
var chain = [
function() {
console.log("step1");
fs.stat("f1.js",chain.shift());
},
function(err, stats) {
console.log("step2");
fs.stat("f2.js",chain.shift());
},
function(err, stats) {
console.log("step3");
fs.stat("f2.js",chain.shift());
},
function(err, stats) {
console.log("step4");
fs.stat("f2.js",chain.shift());
},
function(err, stats) {
console.log("step5");
fs.stat("f2.js",chain.shift());
},
function(err, stats) {
console.log("done");
},
];
chain.shift()();
Run Code Online (Sandbox Code Playgroud)
您可以扩展并行进程甚至并行进程链的惯用语:
var fs = require("fs");
var fork1 = 2, fork2 = 2, chain = [
function() {
console.log("step1");
fs.stat("f1.js",chain.shift());
},
function(err, stats) {
console.log("step2");
var next = chain.shift();
fs.stat("f2a.js",next);
fs.stat("f2b.js",next);
},
function(err, stats) {
if ( --fork1 )
return;
console.log("step3");
var next = chain.shift();
var chain1 = [
function() {
console.log("step4aa");
fs.stat("f1.js",chain1.shift());
},
function(err, stats) {
console.log("step4ab");
fs.stat("f1ab.js",next);
},
];
chain1.shift()();
var chain2 = [
function() {
console.log("step4ba");
fs.stat("f1.js",chain2.shift());
},
function(err, stats) {
console.log("step4bb");
fs.stat("f1ab.js",next);
},
];
chain2.shift()();
},
function(err, stats) {
if ( --fork2 )
return;
console.log("done");
},
];
chain.shift()();
Run Code Online (Sandbox Code Playgroud)
Gra*_* Li 15
为此,我非常喜欢async.js.
瀑布命令解决了这个问题:
瀑布(任务,[回调])
运行一系列函数,每个函数将结果传递给数组中的下一个.但是,如果任何函数将错误传递给回调,则不执行下一个函数,并立即调用主回调并显示错误.
参数
tasks - 要运行的函数数组,每个函数都传递一个必须在完成时调用的回调函数(err,result1,result2,...).第一个参数是一个错误(可以为null),任何进一步的参数将作为参数传递给下一个任务.callback(err,[results]) - 一旦完成所有函数,就会运行一个可选的回调函数.这将传递最后一个任务的回调结果.
例
async.waterfall([
function(callback){
callback(null, 'one', 'two');
},
function(arg1, arg2, callback){
callback(null, 'three');
},
function(arg1, callback){
// arg1 now equals 'three'
callback(null, 'done');
}
], function (err, result) {
// result now equals 'done'
});
Run Code Online (Sandbox Code Playgroud)
至于req,res变量,它们将在与函数(req,res){}相同的范围内共享,它包含整个async.waterfall调用.
不仅如此,异步非常干净.我的意思是我改变了很多这样的案例:
function(o,cb){
function2(o,function(err, resp){
cb(err,resp);
})
}
Run Code Online (Sandbox Code Playgroud)
首先:
function(o,cb){
function2(o,cb);
}
Run Code Online (Sandbox Code Playgroud)
然后到这个:
function2(o,cb);
Run Code Online (Sandbox Code Playgroud)
然后到这个:
async.waterfall([function2,function3,function4],optionalcb)
Run Code Online (Sandbox Code Playgroud)
它还允许从util.js非常快速地调用为async准备的许多预制函数.只需将您想要做的事情链接起来,确保o,cb得到普遍处理.这大大加快了整个编码过程.
gbl*_*zex 11
你需要的是一些语法糖.谢谢这个:
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/html'});
var html = ["<h1>Demo page</h1>"];
var pushHTML = html.push.bind(html);
Queue.push( getSomeData.partial(client, pushHTML) );
Queue.push( getSomeOtherData.partial(client, pushHTML) );
Queue.push( getMoreData.partial(client, pushHTML) );
Queue.push( function() {
res.write(html.join(''));
res.end();
});
Queue.execute();
});
Run Code Online (Sandbox Code Playgroud)
很整洁,不是吗?你可能会注意到html变成了一个数组.这部分是因为字符串是不可变的,所以最好在数组中缓冲输出,而不是丢弃更大和更大的字符串.另一个原因是因为另一个很好的语法bind.
Queue在这个例子中,这只是一个例子,partial可以实现如下
// Functional programming for the rescue
Function.prototype.partial = function() {
var fun = this,
preArgs = Array.prototype.slice.call(arguments);
return function() {
fun.apply(null, preArgs.concat.apply(preArgs, arguments));
};
};
Queue = [];
Queue.execute = function () {
if (Queue.length) {
Queue.shift()(Queue.execute);
}
};
Run Code Online (Sandbox Code Playgroud)
自从我找到它之后就爱上了Async.js.它具有async.series可用于避免长嵌套的功能.
文档: -
串行运行一系列函数,每个函数在前一个函数完成后运行.[...]
tasks - 要运行的函数数组,每个函数都传递一个必须在完成时调用的回调函数.
callback(err, [results]) - 所有功能完成后运行的可选回调.此函数获取传递给数组中使用的回调的所有参数的数组.
以下是我们如何将其应用于您的示例代码: -
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/html'});
var html = "<h1>Demo page</h1>";
async.series([
function (callback) {
getSomeData(client, function (someData) {
html += "<p>"+ someData +"</p>";
callback();
});
},
function (callback) {
getSomeOtherData(client, function (someOtherData) {
html += "<p>"+ someOtherData +"</p>";
callback();
});
},
funciton (callback) {
getMoreData(client, function (moreData) {
html += "<p>"+ moreData +"</p>";
callback();
});
}
], function () {
res.write(html);
res.end();
});
});
Run Code Online (Sandbox Code Playgroud)
我见过的最简单的语法糖是节点承诺.
npm install node-promise || git clone https://github.com/kriszyp/node-promise
使用此方法可以链接异步方法:
firstMethod().then(secondMethod).then(thirdMethod);
Run Code Online (Sandbox Code Playgroud)
每个的返回值在下一个中作为参数提供.
| 归档时间: |
|
| 查看次数: |
58812 次 |
| 最近记录: |