查看原文
其他

[Node.js] 理解 Node.js 事件驱动

2016-09-27 Larry Lu 前端圈

台湾朋友盧承億投稿



这篇讲的东西比较偏概念,所以文字叙述会多一点,希望大家可以有耐心的看完~


一、Async(非同步)

Node.js 是一个单线程且异步的语言,异步的 function 会被放进一个 event queue,等其他代码运行完之后才会运行那个 event queue。如果 queue 里面有很多事要做就会依序做,所以不会阻塞线程。


二、setTimeout

Javascript 最简单的 async 函数是 setTimeout,setTimeout会在一定的时间之后执行某个函数,或说是在一定时间之后把那个函数放进 event queue,等其他事情都做完就会就会开始做 event queue 内的事情。

范例1

setTimeout(function(){
   console.log('callback');
}, 1000);

console.log('Hello World');

刚开始跑到 setTimeout,系统会设定在 1 秒之后把 function 要做的事放到 event queue,然后就继续跑到下面输出 “Hello World”,输出结束之后这时候事情都做完了。


等了一秒 function 被塞到 event queue,这时候没有事情要做所以就开始运行 event queue 内的事情,然后就输出“callback”。


范例2


setTimeout(function(){
   console.log('callback');
}, 1000);

while(true){
   var a = 1 * 2;
}

这个跟范例1 不太一样,先跑到 setTimeout,系统会设定在 1 秒之后把 function 要做的事放到 event queue,继续往下跑到while,一直算一些东西,等了一秒 function 被塞到 event queue。这时候因为一直有事情要做,所以虽然console.log('callback')已经被放到 event queue,但永远不会执行,因为程序要在做完主要的事情才会开始跑 event queue。


三、Callback

因为不知道 async 的那些工作什么时候会完成,但我们又需要等那些工作做完之后才能做某些事情。比如说如果要判断一个文件里面有多少字符,要先读文件,接着再从读到的字符串计算有多少个字符,但偏偏读文件的函数是 async,这时候就需要 callback


范例1

读文件并计算长度:

var fs = require('fs');

// fs.readFile(filename, callback(err, content))

fs.readFile('test.txt', function(err, content){
   var str = content.toString();
   console.log(str.length);
   console.log('finish');
});

console.log('not finish');

fs 是个 nodejs 的模块,可以用来读文件,跑到fs.readFile的时候,系统会把读文件这个任务放到 event queue,有空的时候就会去做,如果当下可以系统有空也有可能马上读。


接着就继续往下跑输出not finish,因为不知道文件什么时候会读完,所以我们把文件读完要做的事情放在 callback 里面。虽然不知道什么时候会读完,但只要一读完文件就会做callback 里面的事情,计算那个文件里面有多少字符,然后输出finish。


范例2

读完文件在档尾加上Hello World再写回去文件:

var fs = require('fs');

// fs.readFile(filename, callback(err, content))
// fs.writeFile(filename, content, callback(err))

fs.readFile('test.txt', function(err, content){
   var str = content.toString();
   str += 'Hello World';
   fs.writeFile('test.txt', str, function(err){
       console.log('finish');
   });
});

console.log('not finish');

callback 的方式可以一直迭代,读完文件之后在尾巴加上Hello World,之后再写入文件。但写入文件也是 async,所以也要设定一个 callback,写入完成之后再输出not finish


范例3

callback 内检查错误:

var fs = require('fs');

fs.readFile('test.txt', function(err, content){
   if(err) console.log(err);

   var str = content.toString();
   str += 'Hello World';
});

callback 内通常都会有一个err传回,如果不是nullundefined,代表处理过程中有发生错误。比如说要读取test.txt这个文件,结果根本没有这个文件,那就会发生错误。用 callback 一定要检查错误,如果有错误继续做下去可能会发生非预期的结果。


四、自己写一个 async function

我们也可以把一些 async 的操作包起来,自己写一个 async function,然后定义一个 callback


范例

写一个函数叫做 append,功能是在某个文件后面加上某个字符串,因为 append 需要读写文件,所以一定也是个 async 函数。


prototype 可能是append(filename, str, callback(err))

var append = function(filename, str, callback){
 fs.readFile(filename, function(err1, content){
   if(err1) console.log(err1);

   var newContent = content.toString() + str;
   fs.writeFile(filename, newContent, function(err2){
     if(err2) console.log(err2);
       callback(err1 || err2);
   });
 });
};

append('text.txt', 'Hello World', function(err){
 if(err) console.log(err);
 console.log('finish');
});

append内刚开始先读文件接着写文件,等读写都完成之后就呼叫 callback,这样就会运行使用者定义的 callback function

写完之后就可以直接像下面这样用,然后给一个 callback function,在append都完成之后就会运行 callback 然后输出finish


五、Callback Hell

因为使用 callback 可能会一直往上迭,先进行 A 接着跑 B 然后 C, D 等等等,B 需要 A 的结果,C 需要 B 的结果,写出来的代码可能是:


func1(function(err1, result1){
 if(err1){
   console.log(err1);
 } else {
   func2(result1, function(err2, result2, result3){
     if(err2){
       console.log(err2);
     } else {
       func3(result2, result3, function(err3, result4){
         if(err3){
           console.log(err3);
         } else {
           console.log(result4);
         }
       });
     }
   });
 }
});

这样的代码还是对的,只是很丑很难debug,可以用 asyncmodule 来改善,避免 code 越迭越高,可以参考(http://larry850806.github.io/2016/05/31/async/)


不然也可以用Promise,不过那比较难懂,建议完全理解 callback 再开始摸 Promise。


六、总结

整个程序只有一个 event queueasync function 都会被塞到 event queue,系统有空就开始运行 event queue,里面的所有任务会轮流运行,运行完就呼叫 callback


Javascript callback 机制跟其他语言不一样,像 C++ / Java 都是一行一行运行(如果只有单线程),但 js 因为有一堆 callback 常常不知道现在在运行哪一段代码,刚开始会觉得有点乱,但久了之后觉得还蛮好用的,不用自己去确认某件事做完了没,反正做完会有 callback


Node.js 的事件驱动机制介绍就到这里。


【读书会往期回顾】

一、[活动]中秋读书会,你买书,我付钱!

二、[活动]中秋读书会获奖名单公布

三、[活动]《CSS揭秘》签名版等您拿,第二期读书会来啦!


【React启蒙系列文章】

一、[React启蒙系列] 初探React

二、[React启蒙系列] 学习React前需要理解的名词

三、[React启蒙系列] React和Babel的基本使用

四、[React启蒙系列]React节点

五、[React启蒙系列]使用JSX

六、[React启蒙系列]React 组件属性

七、[React启蒙系列]React 组件状态


【您可能感兴趣的文章】

一、手把手教你用react

二、React入门及资源指引

三、利用ESLint检查代码质量

四、构建一个安全的 JavaScript 沙箱

五、入门Webpack,看这篇就够了

六、第三届CSS大会广州找场地啦~~求介绍~~

七、Web Components 是个什么样的东西

八、JavaScript 被忽视的细节

九、[心得] 如何提高 React App 的性能

十、[心得] 自己动手打造 ES7 开发环境

十一、[译文] 如何运用新技术提升网页速度和性能

十二、你应该知道的HTTP基础知识

十三、Webpack from First Principles

十四、没时间阅读?佐克伯、比尔盖兹、马斯克教你「5小时原则」

十五、没快速成长,别说你在创业

十六、TCP三次握手&Render Tree页面渲染=>从输入URL到页面显示的过程?

十七、前端知识普及之HTML

十八、[翻译]React最佳实践与实用函数

十九、[翻译]从JS模块化现状阐释选择ES6模块的重要性

二十、前端进阶-让你升级的网络知识

二十一、GitHub 2016 年度开源项目报告导读

二十二、正则之基本入门

二十三、你所不知道的 Console

二十四、谈谈React

二十五、[Javascript] 关于 JS 中的浅拷贝和深拷贝

二十六、准时!英国维珍集团大老给上班族的唯一忠告

二十七、前端知识普及之页面加载




前端圈--打造专业的前端技术会议

为web前端开发者提供技术分享和交流的平台

打造一个良好的前端圈生态,推动web标准化的发展

官网:http://fequan.com

微博:fequancom | QQ群:41378087


长按二维码关注我们

投稿:content@fequan.com

赞助合作:apply@fequan.com


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存