`
天梯梦
  • 浏览: 13757105 次
  • 性别: Icon_minigender_2
  • 来自: 洛杉矶
社区版块
存档分类
最新评论

Node.js入门 - 回调函数

 
阅读更多

本文自theprojectspot.com翻译而来,文章原作者为Lee Jacobson, 已经作者授权翻译用于非商业用途。原文地址:猛戳这里进入

 

如果你还没读过第一篇,先跑到这里去瞧一瞧,我保证你多花这么一点时间是值得的。

 

到目前为止,我们已经学会了用Node.js做一些基本的事(比如用来打印金光闪闪的Hello World), 接下来我们将去学习一下回调函数并了解为什么这玩意是如此的有用。

 

为什么是Node.js?

现在已经有很多编程语言,并且各有各的优缺点。能够对比不同编程技术,并选出最佳编程语言来解决问题,是创建一个可靠的产品的重要前提条件。跟所有别的编程语言一样,Node.js有它自身的优点和缺点,那么让我们来看一下应该在什么情况下使用Node.js吧。

 

优点:

  • a) Node.js用于解决高并发带来的瓶颈。比如你要创建一个需要短时间内处理成千上万的请求,那么Node.js无疑是一个很棒的选择。
  • b) 这就是JavaScript,这意味着任何有前端JavaScript编程经验的人都可以把以前的经验带到Node.js编程里来,并且它是基于Google的V8引擎,速度极快。
  • c) 如果你在客户端和服务器端同时使用JavaScript,这会让你的编程变得轻松和快速。比如:你如果要做一些校验,服务器端和客户端可以共用一样的代码。而前后端数据结构一致,数据传输时超容易。
  • d) 你会让那些做PHP的同志们惊讶得下巴掉下来。(这哥们黑PHP可真是不遗余力啊。)

光说好的也不行对吧,让我们来看一下不好的。

缺点:

  • a) 这是一门新技术,开发者们还来不及创建健壮的测试模型,也还没有好用的IDE出现。并且当出现问题时,能找到的现成解决方案和文档要比像PHP一样的技术 少得多。虽然在当下这是一个劣势,但随着这门技术越来越流行,相信假以时日,就会积累够多的资源可以快速轻松地开发。
  • b) 代码容易写的混乱(JavaScript的先天属性),要让Node.js程序很好地运行,要用很多匿名函数和回调,而回调函数的一大缺点就是容易让你的逻辑被打得支离破碎,程序越大,给阅读和调试带来的麻烦就越多。
  • c) 不能很好地处理数字(不确定这句话的意思,原文是It's not the best number cruncher), 可是PHP或者Ruby同样有这个问题!(这次把Ruby也带上一起黑了)。

了解Node.js和优势和劣势这点很重要,这才能让你决定Node.js是不是满足你的需求的最佳解决方案。不过要是你的代码像狗屎一样,你选啥 技术也只会写出狗屎一样的程序来,这已经不是技术方案优劣的讨论范畴了。在学习回调之前,让我们先快速了解一下阻塞(blocking)和非阻塞 (none-blocking)模型,以及对于我们代码性能的影响。

 

阻塞和非阻塞(Blocking and none-blocking)

阻塞指的是因为别的东西阻止你当前代码的执行,通常发生在你的应用在等待其它资源,比如CPU,网络,内存和硬盘等。为了更好地解释阻塞,我们来看一下以下代码:

    var http = require('http');
    var url = require('url');

    http.createServer(function (request, response) {
        response.writeHead(200, {'Content-Type': 'text/plain'});

        if( url.parse(request.url).pathname == '/wait' ){
            var startTime = new Date().getTime();
            while (new Date().getTime() < startTime + 15000);
            response.write('Thanks for waiting!');
        } else{
            response.write('Hello!');
        }

        response.end();
    }).listen(8080);

    console.log('Server started');

 

把文件保存为"blocking.js",并在终端中执行命令"node blocking.js"。

 

现在我们运行了一个阻塞脚本,当用户访问'/wait'的时候,将运行一个循环,15秒之后打出一句话"Thanks for waiting!"。先打开浏览器,在地址栏输入http://localhost:8080/wait,然后再直接访问http://localhost:8080(记住时间尽量短,不要超过15秒哦)。这时候你应该发现即使是切换到了第二个url,但是牛B的"Hello"还是要等那15秒执行完毕后才打出来,这是肿么了?

 

Node.js被设计成单线程执行模型,这让它的表现异于其它Web开发技术,比如PHP会为每个连接创建一个新的线程。而在Node.js里,如果前一个请求需要耗时5秒钟,则后一个请求必须等待5秒后才能执行。我们把这个叫“阻塞”,前一个线程“阻塞”了后一个线程。

 

这通常在多数Web场景中是不可接受的,那怎么来避免“阻塞”呢?首先我们要让自己的代码变成“非阻塞”的,为了实现这个功能我们要用到回调函数。

 

什么是回调?

如果你以前有过使用JavaScript的经验,你应该对回调函数已经有所了解。让我们来想像一个基本场景,我们要干某件耗费大量时间的事,比如试 图读取一个大文件,我们不想让Node.js服务必须读完这个文件才能响应别的请求,为了完成这样的功能,我们最好是告诉Node.js在后台读取这个文 件,当读取完之后通过某种方式通知我们,而在这个过程中, 服务器同时还能处理别的请求,也就是使我们的代码变成“非阻塞”,就像我们刚刚举的例子一样,以适用于同时时间处理多个请求的情况,我们也把这个叫做异步 调用。

 

为了更清楚说明"非阻塞","异步"是怎么工作的,我们来看一下下面这个例子:

你在一个狭窄的道路上开车,前面有一个SB在停着打电话,很忙的样子(阻塞代码)使你不能到达目的地,这样你必须等这个SB打完电话把车启动起来才 能继续(有人可能想,用板砖干他丫的,但从程序角度来说,把他丫干死,前面少了一个司机,你要等警察来拖走或者自己先开走他的后再开自己的车,外加法律责 任,代价是很大滴,这叫破坏模型,比阻塞模型的代价还大)。

 

想像一下如果这条路上有紧急停车带,前面那SB司机可以变得不SB,先把车停在紧急停车带打电话。把路让出来让你先继续你的旅程。当那个不再SB的 司机打完电话之后也可以回到主干上来继续前行,还避免了可能碰到的板砖型程序员而导致血光之灾,皆大欢喜。这跟异步调用很像,在同一时间同一主干上跑多辆 车。

 

好了,现在用我们新学到的知识来写"非阻塞"代码吧,还是实现刚才那个功能。

 

首先放一段阻塞代码在一个新文件里:

    var startTime = new Date().getTime();
    while (new Date().getTime() < startTime + 10000);

 

保存为"block.js"。

接下来创建服务:

    var http = require('http');
    var url = require('url');
    var cp = require('child_process');

    function onRequest(request, response) {
        var pathname = url.parse(request.url).pathname;
        if( pathname == '/wait' ){
            cp.exec('node block.js', myCallback);
        }
        else{
            response.writeHead(200, {'Content-Type': 'text/plain'});
            response.write('Hello!\n');
            response.end();
        }

        console.log('New connection');

        function myCallback(){
            response.writeHead(200, {'Content-Type': 'text/plain'});
            response.write('Thanks for waiting!\n');
            response.end();
        }
    }
    http.createServer(onRequest).listen(8080);
    console.log('Server started');

 

这里我们使用了"子线程"模块来调用新的线程,也就是阻塞程序block.js,而我们的主线程则可以高高兴兴地处理别的请求。我们调用子线程模块 的.exec函数启动阻塞线程并运行之,.exec()函数有两个参数第一个是调用block.js的node命令,第二个是回调函数。当.exec() 执行完毕之后会调用回调函数myCallback打印"Thanks for waiting!"。

 

把上面的代码保存为"nonblocking.js",然后执行"node nonblocking.js"测试。

 

现在你应该注意到,在先访问http://localhost:8080/wait 后访问http://localhost:8080这个过程中,后者不再等待前者15秒了。很明显这又是一个没啥意思的程序,"Thanks for waiting!"这几个字并不比"Hello World!"要有趣多少。那么,让我们利用上述结构的程序来写一个稍微有那么一点意思的程序吧。

 

幸运的是Node.js内置很多非阻塞的异步回调以供我们在应用程序中方便地使用,下面是一个非阻塞文件读取器的代码:

    var http = require('http');
    var fileSystem = require('fs');

    http.createServer(function (request, response) {
        response.writeHead(200, {'Content-Type': 'text/plain'});

        var read_stream = fileSystem.createReadStream('myfile.txt');
        read_stream.on('data', writeCallback);
        read_stream.on('close', closeCallback);

        function writeCallback(data){
            response.write(data);
        }

        function closeCallback(){
            response.end();
        }

    }).listen(8080);

    console.log('Server started');

 

这里我们使用了Node.js内置的文件系统模块,用.createReadStream()读取我们的文件,并绑定两个函数到"data"和"close"事件。这些函数将在事件引发时执行。

 

把上述代码存为"fileReader.js",在同级目录下新建一个文本文件"myfile.txt", 执行"node fileReader.js"命令,现在你可以在http://localhost:8080 看到浏览器里显示了myfile.txt内容了。

 

总结

无论你是否有需要执行一个耗时很长的程序与否,你都应该使用非阻塞模型,并且记住,正确使用回调和异步可以让代码的速度和稳定性都能得到提高。

 

转自:http://www.laonan.net/blog/64/

分享到:
评论

相关推荐

    Node.js开发-Node.js入门.pdf

    Node.js 维护一个事件队列,需执行的每个任务都会加入事件队列,并提供一个包含处理结果的回调函数。 Node.js 的应用场合包括: * 实时数据处理 * 高性能 Web 应用程序 * 数据分析和科学计算 * 实时通信和游戏...

    Node.js入门经典 源代码

    1. 异步编程:Node.js主要依赖回调函数进行异步编程,但这种模式可能导致回调地狱。后来引入了Promise和async/await,使得异步代码更易读、更易于管理。 2. 模块系统:Node.js使用CommonJS规范,通过require导入模块...

    《Node.js入门到精通》--源代码(2015.09.04)

    这意味着当进行I/O操作时,程序不会停止执行其他任务,而是继续处理其他工作,直到I/O操作完成后再回调函数中处理结果。这极大地提高了程序的运行效率。 4. **MongoDB和Mongoose**:MongoDB是一个流行的NoSQL数据库...

    2013年Node.js入门经典源代码

    当一个I/O操作完成时,事件会被触发,对应的回调函数会被放入事件队列,等待主线程调用执行。 3. V8引擎:Node.js内部使用Google的V8引擎,能够高效地执行JavaScript代码,提供了接近原生应用的性能。 4. 模块系统...

    《Node入门一本全面地Node.js教程》PDF

    - **回调函数**:Node.js中的异步操作通常通过回调函数实现,但过度使用可能导致回调地狱问题。 - **Promise**:Promise是解决回调地狱的工具,提供了链式调用和错误处理机制。 - **async/await**:基于Promise的...

    Node.js入门经典源代码

    通过事件循环和回调函数,Node.js可以高效地处理I/O操作,而不会像传统的多线程模型那样消耗大量系统资源。 2. **模块系统**: Node.js使用CommonJS模块规范,通过`require`导入模块,`module.exports`或`exports`...

    新时期的Node.js入门.pdf

    7. **事件和回调**:事件驱动是Node.js的核心,理解事件发射器和事件监听器的工作原理至关重要。 8. **Promise和async/await**:现代JavaScript中的异步编程解决方案,如何用它们来编写更加清晰和易于维护的代码。 ...

    Node.js-node.js中文资料导航

    7. **Event Loop(事件循环)**:Node.js 的核心机制之一,它负责调度异步操作,将完成的任务放入回调函数队列,并在合适的时机执行,实现事件驱动的编程模型。 8. **Promise 和 Async/Await**:Node.js 从 v8.0.0 ...

    Node.js入门源代码及开发工具

    理解回调函数、Promise和async/await,对于编写高效的Node.js代码至关重要。 开发工具方面,通常包括: 1. **Node.js安装**:你需要在本地安装Node.js环境,以运行和调试代码。官网下载最新版本,安装后即可在...

    Node.JS入门.docx

    Node.js 提供了多种事件驱动编程模型,如回调函数、 promise 和 async/await 等,每种模型都有其优缺点。 构建完整的 web 应用 ------------------- 本书带领读者一步一步地完成一个完整的 web 应用,包括创建...

    node.js入门中文版

    《Node.js 入门中文版》是一本专为开发者准备的指南,旨在教授如何使用 Node.js 开发应用程序,尤其适合那些具有其他面向对象语言(如 Ruby、Python、PHP 或 Java)经验,但对 JavaScript 和 Node.js 初步接触的读者...

    node.js入门

    错误处理在Node.js中非常重要,通常通过try/catch、回调函数中的错误参数、Promise的catch方法等方式进行。 13. **测试** 对于Node.js应用,可以使用Mocha、Jest等测试框架编写单元测试和集成测试,确保代码质量...

    Node入门 » 一本全面的Node.pdf

    - 理解异步编程,特别是回调函数(Callbacks)、承诺(Promises)和async/await的使用。 3. Node.js模块系统 - 掌握CommonJS模块规范,理解module.exports和exports的用法。 - 学习使用Node.js内置模块,如fs、...

    Node.js入门

    在本书《Node.js入门》中,作者Manuel Kiessling 针对那些至少已经掌握一门面向对象编程语言,并且对JavaScript有一定了解但并非专家的读者。本书不是针对JavaScript初学者,因此不会讲解JavaScript的基本概念,如...

    node.js入门手册

    9. **异步编程**:Node.js的核心就是异步编程,包括回调函数、Promise和async/await。手册会深入讲解这些异步处理方式,以及如何避免回调地狱。 10. **NPM(Node Package Manager)**:NPM是Node.js的包管理器,...

    NODE.JS入门手册.zip

    1. **事件驱动编程**:Node.js的基础架构是基于事件循环和回调函数的。了解如何使用事件监听器和处理器来构建高效、响应迅速的系统至关重要。 2. **V8引擎**:Node.js使用Google的V8引擎执行JavaScript,这使得其...

Global site tag (gtag.js) - Google Analytics