在Node.js应用程序中使用q.js的q.all实现并行异步性

4
背景简介:我已经编写了下面的代码来指示node.js对三个文本文件进行操作,从而组装一个HTML页面,这些文本文件分别是页面头部、页面主体和页面页脚组件。显然,这些页面片段必须按正确的顺序组装。另一方面,在将它们组装之前,完全没有要求从它们的文本文件中按任何顺序提取这些页面片段。我想利用这一点通过实现并行异步性。
实现代码:
var sys = require('sys'),
http = require('http'),
fs = require('fs'),
Q = require('q'),
fullpage, pageheader, pagebody, pagefooter;

fullpage = '';

fs_readheader = fs.readFile('./htmlfiles.1', 'utf-8', function (err, data) {
   if (err) { throw err; }
   return pageheader = data;
});

fs_readbody = fs.readFile('./htmlfiles.2', 'utf-8', function (err, data) {
  if (err) { throw err; }
  return pagebody = data;
});

fs_readfooter = fs.readFile('./htmlfiles.3', 'utf-8', function (err, data) {
  if (err) { throw err; }
  return pagefooter = data;
});

finish = function(err, data) {
  if (err) { throw err; }
  console.log(pageheader);
  data = pageheader + pagebody + pagefooter;
  console.log(data);
  return fullpage = data;
}();


Q.all([fs_readheader,fs_readbody,fs_readfooter]).then(finish);


http.createServer(function(request, response) {
  response.writeHeader(200, {"Content-Type": "text/html"});
  response.write(fullpage);
  response.end();
}).listen(8001);

问题:node.js服务器在127.0.0.1:8001上没有显示任何内容。我当然期望看到完整的HTML页面。我正在使用q.js的1.00版本,这是此时最新版本的q.js。
后续: 作为对Barry-Johnson全面意见的回应,我已将其代码修改为以下形式:
var http = require('http'),
  fs = require('fs'),
  Q = require('q');

var readFileP = Q.denodeify(fs.readFile);

http.createServer(function(request, response) {
  Q.all([ readFileP('./htmlfiles.1', 'utf-8'),
          readFileP('./htmlfiles.2', 'utf-8'),
          readFileP('./htmlfiles.3', 'utf-8') ])
    .done(function(content){
        response.writeHead(200, {"Content-Type": "text/html"});
        response.end(content.join(''));
    }); 

}).listen(8001);

结果正是我想要的,一个按正确顺序完整组装的HTML页面。我在“join”方法中添加了''作为参数,因为我不想让这些部分用逗号连接。我使用了“done”方法,因为我希望任何未捕获的错误能够浮现出来。我知道,在一般情况下,我们不希望任何生产代码崩溃,Barry-Johnson的“fail”方法实现这一目的很好。

Promise回调函数不需要传递错误参数。 - SLaks
@Slaks - 你是对的,因为Promise链旨在准确地重现回调链,条件是错误必须传递到Promise链的最后一个链接(“done”或“fail”),在那里进行处理。要求错误在Promise链的最后一个链接或节点中进行处理意味着任何类似节点的函数,如果要成为Promise链的一部分,则必须使用“promisified”将其从中取出错误元素,然后才能将其放置在Promise链中。链中的最后一个链接中的函数未经过“promisified”。 - Vietnhi Phuvan
1个回答

8
SLaks关于Promise的说法是正确的。更一般地说,要么你复制/粘贴出了问题,要么Promise问题在你正在处理的许多其他问题中是次要的。仅使上述代码中的Promise工作并不能得到我认为你正在寻求的结果。
然而,集中精力解决Promise问题-你需要查看Q.doc的Adapting Node部分,例如Q.denodeify、Q.ninvoke、Q.nfapply等调用,这些调用允许您使用遵循具有尾随回调的标准节点约定的函数,并将它们作为返回Promise的函数进行操作。
好消息是-你需要处理的代码少了很多。以下是带有一些注释并保留了一些原始冗长性的版本:
var sys = require('sys'),
    http = require('http'),
    fs = require('fs'),
    Q = require('q'),
    fullpage, pageheader, pagebody, pagefooter;

var readFileP = Q.denodeify(fs.readFile);

var fsReadheader = readFileP('./file1.html', 'utf-8');
var fsReadBody = readFileP('./file2.html', 'utf-8');
var fsReadFooter = readFileP('./file3.html', 'utf-8');

//  This is where you had Q.all before - which is probably not what you want,
// or did you want to just build the page once and have it static for the life
// of the app? If that is the case, promises are kind of pointless and you may
// as well K.I.S.S. and just read the bits synchronously at startup

http.createServer(function(request, response) {
    // you should probably wait to write out the response until you know if you have success.
    // but again, maybe you do want this to be totally static content
    response.writeHead(200, {"Content-Type": "text/html"});
    // I am assuming you wanted to load the files at request time, possibly
    // in preparation to adapting your code to make ti more dynamic.
    Q.all([fsReadheader,fsReadBody,fsReadFooter])
        .then(function (x) {
            // this was your old 'finish' function - doesn't really do much.
            // I used join instead of concatenating. less typing. And no typing
            // if you ever add file4
            return x.join();})
        .then(function(content){
            // You could response.write() as well, but you can send data with .end()
            response.end(content);
        })
        .fail(function(e){
            // here is where your error handler goes. any error will end up here.'
            // you could have more granular erro handling
            response.writeHead(500, 'Bad news, Tex');
            response.end('No dice');
        });
    //
}).listen(8001);

然而,除非我按击键数获得报酬,否则我会这样写:

var http = require('http'),
    fs = require('fs'),
    Q = require('q');

var readFileP = Q.denodeify(fs.readFile);

http.createServer(function(request, response) {
    Q.all([ readFileP('./file1.html', 'utf-8'),
            readFileP('./file2.html', 'utf-8'),
            readFileP('./file3.html', 'utf-8') ])
        .then(function(content){
            response.writeHead(200, {"Content-Type": "text/html"});
            response.end(content.join());
        })
        .fail(function(e){
            response.writeHead(500, 'Bad news, Tex');
            response.end('No dice');
        });
    //
}).listen(8001);

感谢您的全面输入。我已经进一步简化了代码。代码背后的想法是,我将组合静态头和脚的HTML页面,其内容是任意和动态的。我是新手 - 这显然很痛苦 - 对于承诺,我对承诺的前景感到兴奋 :) - Vietnhi Phuvan
2
非常欢迎。如果解决方案满足了您的需求,您可以接受答案。关于您的主要目标(静态页眉/页脚与动态页面内容)- 您可以查看 express.js,它支持Jade、EJS或其他类型的模板。或者使用上面的基本代码,对于真正的静态页眉/页脚,您只需在启动时将页眉和页脚加载到变量中,然后在请求时仅加载动态内容并将其与页眉/页脚变量连接起来。无论如何,祝您好运。 - barry-johnson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接