2016-03-01 73 views
1

我已经成功地编写了几个nodejs HTTP处理程序来响应HTTP请求提供数据。但是,我写的所有内容都使用* Sync函数版本。我现在很快遇到了这种方法的局限性。使用异步Node.JS来服务HTTP请求

但是,我不知道如何在HTTP请求上下文中正确使用异步函数。如果我尝试一个异步调用,处理很快就会通过并返回,而不会给代码处理数据的机会。

什么是正确的方法?我还没有找到任何好的例子,所以任何指向文学的指针都是值得赞赏的。除此之外,GET请求处理程序的一个例子是扫描本地目录,例如返回一个json的文件名列表和相应的行数(或者真正的上述任何存根代码,显示正确的技术) 。

+0

你必须在异步函数 –

+0

的回调中完成所有工作。我知道这一点。我如何确保请求处理程序在所有回调完成之前都不会返回(不通过)。 –

+0

有很多不同的方法来做到这一点。我会建议与ES7 transpilation +异步/等待。如果你不想经历麻烦,那么[co](https://github.com/tj/co)/'yield'就相当接近。我仍然建议使用内置于节点的[Promises](https://github.com/petkaantonov/bluebird),但我链接的蓝鸟库可能会提示不支持promise的库。我强烈推荐Promises ...我认为它们可以简化异步流程。 [异步](https://github.com/caolan/async)也很受欢迎。最后,你可以推出自己的。 –

回答

2

下面是一个简单的示例:

node server.js
var http = require('http') 
var fs = require('fs') 

function dir (req, res) { 
    fs.readdir('.', function (error, files) { 
    if (error) { 
     res.writeHead(500) 
     res.end(error.message) 
     return 
    } 
    files.forEach(function (file) { 
     res.write(file + '\n') 
    }) 
    res.end() 
    }) 
} 

var server = http.createServer(dir) 
server.listen(7000) 

运行,并与curl :7000测试。

是的请求处理程序在执行readdir回调之前返回。这是设计。这就是异步编程的工作原理。没关系。当文件系统IO完成时,回调将执行并且响应将被发送。

+0

好的,这是有道理的。然而,我真的有一个心理障碍扩展到一个更复杂的情况下,我嵌套了异步处理程序的水平,例如读取每个文件的内容来计算行和/或为多个(子)目录执行此操作。 –

+0

异步控制流程需要从同步代码进行大量调整。尝试nodechool.io教程,callbackhell.com,阅读Github上的开源项目等。它最终会“点击”。 –

1

彼得里昂的答案是伟大的/正确的。我将稍微扩展一下,并提出一种使用承诺和co以及嵌套/循环异步性的不同方法。

/* Script to count all lines of a file */ 
const co = require("co"); 
// Promisifed fs -- eventually node will support this on its own 
const fs = require("mz/fs"); 
const rootDir = 'files/'; 

// Recursivey count the lines of all files in the given directory and sum them 
function countLines(directory) { 
    // We can only use `yield` inside a generator block 
    // `co` allows us to do this and walks through the generator on its own 
    // `yield` will not move to the next line until the promise resolves 
    // 
    // This is still asynchronous code but it is written in a way 
    // that makes it look synchronized. This entire block is asynchronous, so we 
    // can `countLines` of multiple directories simultaneously 
    return co(function*() { 
     // `files` will be an array of files in the given directory 
     const files = yield fs.readdir(directory); 

     // `.map` will create an array of promises. `yield` only completes when 
     // *all* promises in the array have resolved 
     const lines = yield files.map(file => countFileLines(file, directory)); 

     // Sum the lines of all files in this directory 
     return lines.reduce((a, b) => a + b, 0); 
    }); 
} 

function countFileLines(file, directory) { 
    // We need the full path to read the file 
    const fullPath = `${directory}/${file}`; 

    // `co` returns a promise, so `co` itself can be yielded 
    // This entire block is asynchronous so we should be able to count lines 
    // of files without waiting for each file to be read 
    return co(function*() { 
     // Used to check whether this file is a directory 
     const stats = yield fs.stat(fullPath); 
     if (stats.isDirectory()) { 
      // If it is, recursively count lines of this directory 
      return countLines(fullPath); 
     } 
     // Otherwise just get the line count of the file 
     const contents = yield fs.readFile(fullPath, "utf8"); 
     return contents.split("\n").length - 1; 
    }); 
} 

co(function*() { 
    console.log(yield countLines(rootDir)); 
}) 
// All errors propagate here 
.catch(err => console.error(err.stack)); 

请注意,这只是一个例子。可能已经有库来计算目录中的文件行数,并且确实存在能够简化递归读取/文件匹配的库。

+1

圣牛!自从它开始以来,我一直在为...开启和关闭JavaScript,但看起来像在最后一次关闭期间,语言经历了一次真棒突变。 –