本来学oc学的正爽,结果由于某些原因必须先学习NodeJS,所以oc的学习就先暂停一段时间吧。这几天恶补了相关基础知识,如果oc只是语法上让我不太习惯的话,那nodejs就是从编程思想上将原来所学的知识进行了颠覆。至于nodejs相关介绍、优缺点等这里就不多说了,如果想在学习过程中不“一头雾水”,就要理解几个概念:同步/异步、阻塞/非阻塞、闭包、回调、事件轮循。

本人以前并没有任何javascript语言的学习经验,这里仅仅记录我这几天学习的理解,如有什么偏颇,还望大家指正。

首先,nodejs是单线程的,同时只能进行一项任务。这点引起较大的批评就是无法利用多核CPU的能力,但同时在多个CPU运行程序也并不是完美的,因为想要多个CPU有效的拆分任务并执行,它们之间需要频繁的交换信息,比如当前执行状态、各自完成了哪些操作等。

关于上面的几个概念,网上答案五花八门都有,不过我更倾向于下面的答案,引用于知乎,原文链接

“阻塞”与”非阻塞”与”同步”与“异步”不能简单的从字面理解,提供一个从分布式系统角度的回答。

1.同步与异步

同步和异步关注的是消息通信机制 (synchronous communication/ asynchronous communication)

所谓同步,就是在发出一个调用时,在没有得到结果之前,该调用就不返回。但是一旦调用返回,就得到返回值了。

换句话说,就是由调用者主动等待这个调用的结果。

而异步则是相反,调用在发出之后,这个调用就直接返回了,所以没有返回结果。换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果。而是在调用发出后,被调用者通过状态、通知来通知调用者,或通过回调函数处理这个调用。

典型的异步编程模型比如Node.js

举个通俗的例子:

你打电话问书店老板有没有《分布式系统》这本书,如果是同步通信机制,书店老板会说,你稍等,”我查一下”,然后开始查啊查,等查好了(可能是5秒,也可能是一天)告诉你结果(返回结果)。

而异步通信机制,书店老板直接告诉你我查一下啊,查好了打电话给你,然后直接挂电话了(不返回结果)。然后查好了,他会主动打电话给你。在这里老板通过“回电”这种方式来回调。

2.阻塞与非阻塞

阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态.

阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。
非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。

还是上面的例子,

你打电话问书店老板有没有《分布式系统》这本书,你如果是阻塞式调用,你会一直把自己“挂起”,直到得到这本书有没有的结果,如果是非阻塞式调用,你不管老板有没有告诉你,你自己先一边去玩了, 当然你也要偶尔过几分钟check一下老板有没有返回结果。

在这里阻塞与非阻塞与是否同步异步无关。跟老板通过什么方式回答你结果无关。

这里补充一下,老板给你打电话回来,然后你去取书就是”回调“。上面网友回答也说道了要偶尔check一下返回结果,个人的理解这就是事件轮循。并不是说这个check这个动作,而是你要找老板这个事件。比如你先去一边玩去了,然后觉得饿了给老妈打电话问饭做好了没,老妈说还没好,你就又继续玩去了。那么事件列表中此时就有2个事件:给老板打电话(确认书到没到)和给老妈打电话(确认饭做没做好)。

再说闭包,什么是闭包(closure)呢?这里又不得不提起“作用域”这个东东了。类比c++、c#、java来说,javascrept中函数内(域内)的成员是不能在域外访问的,类似于”私有成员“。那么怎么得到内部数据呢?这时,闭包就华丽丽的出场了。英语好的小伙伴可以直接看这里,我在下面直接引用了其中比喻非常好的那个小公主的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function princess() {
var adventures = [];
function princeCharming() { / ... / }
var unicorn = { / ... / },
dragons = [ / ... / ],
squirrel = "Hello!";
adventures.push(unicorn, dragons, squirrel);
return {
story: function() {
return adventures[adventures.length - 1];
}
};
}
var littleGirl = princess();
x = littleGirl.story();
y = littleGirl.dragons;
console.log(x);
console.log(y);

执行后结果是

1
2
3
localhost :: ~/mynode/v55 » node test.js
Hello!
undefined

可以看到,闭包是个函数,它可以让你在作用域访问到作用域内部的变量。这里多说一句,虽然javascrept中并没有”类“这一结构,但上面的例子明显就是一个”类“的实现。关于孩子取垃圾袋的类比也不错,代码如下

1
2
3
4
5
6
7
8
9
10
11
12
function makeKitchen () {
var trashBags = ['A', 'B', 'C']; // only 3 at first
return {
getTrashBag: function() {
return trashBags.pop();
}
};
}
var kitchen = makeKitchen();
kitchen.getTrashBag(); // returns trash bag C
kitchen.getTrashBag(); // returns trash bag B
kitchen.getTrashBag(); // returns trash bag A

这个例子中看出,闭包不单可以取得值,还能改变它。这里不得不佩服歪国人那强大的类比能力,正如爱因斯坦说的:

If you can’t explain it to a six-year-old, you really don’t understand it yourself.

至于更底层的细节,比如inx和windows系统中实现异步的库并不一样啦、内存分配回收神码的,作为初学者的我也不懂,有兴趣的同学可以自行研究。

在fedora21中使用’yum install nodejs’安装即可,会自动把npm管理工具安装上,npm就是python的pip,负责三方模块的安装。当然也可以去官网下载源码或相应安装包进行安装。安装完毕后使用”node -v”可以查看版本,这里我安装的是0.10.36。接下来,直接使用node命令进入Node解释器,老规矩,先”hello world“一下:

1
2
3
$ node
> console.log("hello world!")
hello world!

至于上面说的“同一时刻只能执行一项操作”从下面的代码可以加深理解:

1
2
3
4
5
6
7
eve = require('events').EventEmitter;
e = new eve();
die = false;
e.on('die',function(){die = true;});
setTimeout(function(){e.emit('die')},1000);
while(!die){}
console.log("done");

上面的代码永远不会输出done,虽然我们设定了1秒钟后通过事件回调将die设为true,但while循环在这里是永远执行不完的,回调事件永远没机会被触发。