这才是JavaScript的本质!

01

我是张大胖,这是我第一天上班,说实话我有点紧张。 

想想在校4年,除了熬夜打游戏之外,我学得实在是不怎么样。 

不过我遇上了好时候,计算机行业正处于飞速发展期,毕业后我顺利找了一份工作,老板叫Netscape,没错,就是那个古老的浏览器。 

首次见面,Netscape给我分配了工位,告诉我:“你的任务就是执行JavaScript代码,每次遇到函数调用,就把函数压入你桌子上的栈中。” 

栈?这我听说过,大学的数据结构课讲过,一个先进后出的数据结构, 教材上说用栈可以计算四则运算。奥,对了,还可以对一个二叉树做非递归的中序遍历,至于还有什么用处,老师们也没说,我就不知道了。 

为了让我上手,Netscape给了我一段代码:

function mul(x,y) {
    console.log("x="+x +",y="+y)
    return x*y
}

function square(x) {
    return mul(x , x)
}

square(7)

代码非常简单,就是两个简单的函数调用。 

我小心地接过来,开始运行。 

按照Netscape老板的指示,我给这段代码弄了一个虚构的“包裹”函数"main" ,先压入栈中。

扫描二维码关注公众号,回复: 9417705 查看本文章

main函数要调用square,于是square函数也被压入栈。square调用mul, mul调用console.log  ,于是栈就变成了这个样子:

执行完函数,再把他们从栈中一一弹出,直到栈变空为止。 

很简单嘛!原来大学里学的栈操作还有这么一个用途啊:执行函数调用。 

02

唯一的员工:单线程

过了试用期,我正式开始上岗,每天的工作都是老一套,Netscape老板从网上下载HTML, JavaScript, CSS等文件,然后把JavaScript交给我来执行。 

时间久了,我就觉得很奇怪,公司似乎只有我一个打工的,Netscape老板立的规矩很奇葩:所有的JavaScript代码,不管有多长、多复杂,都由我一个人一行一行地执行。 

难道他不想多招几个人同时并行执行吗,那样就快多了!

他对外宣传起来是一套一套的:JavaScript是一门非常简单的语言, 一定要单线程执行,这样程序员就不用考虑多线程的同步、通信、加锁等问题了。 

听起来很有道理,可是我知道这主要是由于他抠门,不愿意花钱雇更多的员工。 

看看CPU阿甘是8核的, 单线程的话只有一个核心可以使用,经常出现一核有难,多核围观的情况。 

可是喜欢JavaScript的人越来越多,Netscape老板发了财,非常得意,喝醉了就经常吹牛:我这套单线程执行的体系完美无缺,用一个栈搞定一切函数调用!

03

异步函数怎么办?

直到有一天,我遇到这么一段代码:

function hello(){
    console.log("hello after 5 seconds");
}
setTimeout(hello, 5000)

console.log("done")

我第一次遇到了setTimeout这个函数,不知道该怎么处理,老板说这是他的函数,于是我也把它压栈,然后请他去执行。 

执行完setTimeout,再去执行log函数:

log函数执行完了,弹出。 

main函数也执行完了,弹出。 

栈空了!

我觉得有点懵! 

这个setTimeout(hello, 5000)的意思不是说等待5秒以后执行hello函数吗? 

现在栈空了,hello函数没有执行的机会了, hello 函数丢了?! 

Netscape老板酒醒了:“不对啊,你应该把hello函数压入栈中执行啊。” 

我说:“setTimeout是你执行的,只有你才知道5秒钟后把hello函数压入栈中啊!” 

老板拍了一下脑门:“奥,对,原来你都是同步执行代码的,现在要变成异步了,让我想想怎么处理吧。”

04

队列

第二天, 老板又招来一个新人:小李。 

小李的工位就在我的旁边,桌子有一个队列, 这是另外一个重要的、先进先出的数据结构。 

可是他的队列又有什么用呢? 

老板说:小李,我交给你一个重要任务,你要时刻监视旁边张大胖的栈,如果栈空了,就把你队列中的事件拿出来,把事件关联的函数压入栈中,让张大胖去执行。 

小李立刻问道:“可是谁往我的队列中加入‘事件’啊!”

老板说:“那自然是我了!来,我们再来执行下这段代码。”

function hello(){
    console.log("hello after 5 seconds");
}
setTimeout(hello, 5000)

console.log("done")

我又开始了一轮把main,setTimeout,log压栈/出栈的操作。 

不一会儿,我面前的栈就空了。 

我幸灾乐祸地看着老板,他设置了一个定时器,5秒的时间到了,他把一个和hello函数关联的事件放入了小李的队列中。 

小李不敢怠慢,看到我这里栈空了, 立刻从队列中取出事件,把关联的hello函数放入我的栈中。 

既然栈中有了函数,我不得不执行。 

终于,hello函数被执行了, "hello after 5 seconds”被正确输出了。

05

事件队列

我觉得老板的这个做法很是古怪,那个定时器到时间以后,直接把hello函数压入我的栈不就行了?!还非得经过小李中转一下,纯属脱裤子放屁,多此一举。 

老板似乎看透了我的心思,淡淡一笑:你有所不知,我们软件世界讲究职责分离,我这边只是产生事件, 加入小李的队列。

小李承担的职责就是“事件循环”,他监测队列中的事件,然后把相关需要执行的函数(hello函数)加入到你的栈中, 你负责的就是执行了。我们三个人完美配合,共同完成工作。

我还是不解:“为了一个简单的timeout,有必要搞这么复杂的公司组织架构吗?”

“很有必要,将来我还会成立一个Web API的部门,不仅用来处理定时器(Timer)事件,还要实现XMLHttpRequest 的事件,DOM的事件等等,你们知道这些事件之间有什么相同之处吗?”

小李比较机灵:“难道是都支持异步处理,基于事件的编程?”

“没错,张大胖以单线程的方式一步步地执行JavaScript代码,遇到那些耗时的操作,必须通过注册一个回调函数的方式来异步处理,具体的实现办法,就是事件队列和事件循环了 !”

老板吩咐小李把他的想法画成一幅组织架构图,贴到了墙上,新加入的员工,只要能理解这幅图,基本上就可以上手干活了!

Javascript: 一个屌丝的逆袭

Node.js :我只需要一个店小二

JavaScript:一个没有类的世界这么玩转面向对象?

我是一个线程

TCP/IP之大明邮差

一个故事讲完Https

两年,我学会了所有的编程语言!

字节码万岁!!!
CPU 阿甘

上帝托梦给我说:一切皆文件

用故事讲解技术,尽在码农翻身

更多精彩文章,请点击《我写公众号这1460天

发布了443 篇原创文章 · 获赞 2773 · 访问量 31万+

猜你喜欢

转载自blog.csdn.net/coderising/article/details/104284386