该文章是对Philip Roberts在JSConfEU演讲的意译和整理。若有误导,请放弃阅读。原文javascript
演讲视频html
javascript程序员喜欢说各类高逼格的术语:“event-loop”,"non-blocking","callback","asynchronous","single-threaded"和“concurrency”等(译者注:确实如此,一些所谓的面试官在本身对这些概念只知其一;不知其二的状况下也喜欢问这些概念)。java
咱们老是装出一成竹在胸的样子说着这样的话:“不要阻塞event loop”,"你要确保你的代码以60FPS(frames-per-second)的速度去运行",“你这么搞法确定是不行啦,那个函数是一个异步执行的callback啊”。node
若是你是像我同样的怂货,你确定会点点头,表示十分认同的样子。尽管你也不知道他说得对不对,由于你不知道这些术语究竟是啥意思。与此同时的事实是,找到一些讲述javascript如何被解释执行的好资料也是相对艰难的。(现在我花了18个月去作了这方面的研究),因此,咱们一块儿学习一下吧。c++
在一些便利的可视化工具的帮助下,咱们能够很直观地理解到javascript的运行时到底发生了什么。git
你们好,感谢你们莅临side track(译者注:side track是啥?)。it is awesomen to see it packed out in here。你们请允许我伸伸懒腰,这样一来,我整我的看起来不至于那么僵硬。我想跟你们谈谈event loop-event loop究竟是什么鬼, 尤为是javascript的event loop究竟是什么鬼?程序员
首先,请允许我自我介绍一下。就像他(指主持人)说的那样,我在AdnYet工做。AdnYet是美国的一个很不错的软件小厂。若是你们在实时软件方面有需求的话,能够联系咱们。咱们最擅长这方面的研发了。github
言归正传。18个月前,我是一个专业的在职javascript开发者。我经常自问:“javascript是如何运行的呢?”。每到这个时候,个人心里都没有一个很肯定的答案。我听过v8团队,也听过v8做为javascript在chrome的运行时而存在。除此以外,我就一无所知了。我彻底不知道v8具体意味着什么(应该是指v8在浏览器内核的中分工角色是什么),具体又是作什么的。我听过好比“single threaded”这些术语,我也知道我本身目前在用的就是叫“callback”。可是callback的运行原理是怎样的呢?为了对这些习觉得常的概念进行深究,我开始了个人探索旅程。探索过程当中,我一边肯定探索主题,一边查阅资料和在浏览器上作试验。其过程用拟人手法能够简单描述为以下的对话形式:web
后来,我听闻了v8,而且也知道,除了v8和chrome,外界还有其它各类javascript的运行时和浏览器。我就跑去问v8了。面试
基本上就是这样,18个月过去了。通过这18个月的上下求索,我如今终于搞清楚了。而这18个月研究所得出的干货,就是我今天要分享给大家的东西了。我但愿这些干货可以帮助到那些javascript新手去理解“相比于其它编程语言,为何javascript显得那么怪异呢?”,“为何callback让咱们又爱又恨,可是又不可或缺呢?”。若是你是经验丰富的javascript开发者,但愿此次分享能为你提供一个视角去理解你当前所用的运行时是如何工做的。然后,你可以更好地编排你的代码。
当咱们把Chrome中的javascript运行时平台v8单独拎出来看的话,咱们发现它是很好的javascript运行时学习对象。v8主要包含两部分:heap和callstack。heap,是内存分配所发生的地方。call stack就是你的stack frame所在的地方。可是,若是你把v8的源码下载下来,而后在里面全局搜索一下“setTimeout”,“DOM”或者“HTTP request”等字眼,你会发现,它们根本就不在v8的源码里面。这是一路研究下来最让我意外的发现。当咱们提到异步机制时第一反应所想到的东西居然不在那个你自觉得是的v8里面。知道真相的你,不知道眼泪有没有掉下来呢?
18个月的探索下来,我发现我所要研究的主题所涉及到的真的真的是一张很大的知识网。正由于知识网之巨大,之有价值,我才特别地但愿你跟我一道学习学习。这样一来,你就会明白各类围绕v8零散的知识点是什么?咱们已经有了一个v8。如今,给你介绍一个由浏览器提供实现的,叫“web API”的东西,好比DOM,AJAX和 time out(setTimeout和setInterval)之类的东西就是归属于“web API”这里面的。同时,咱们还有神神秘秘的event loop和callback queue。我相信你以前也确定据说过这些术语,但也许你不太懂得这些术语是如何关联起来,有机组成一个知识网的。我打算从最基础,最多见的一些术语开始讲。大家的一部分人可能已经了理解透,另一部分可能并无。我以为大部分人应该是属于后者的。若是你是前者,那么忍受一下个人唠叨吧。
again,javascript是一门single threaded的编程语言。这个single threaded的编程语言的runtime有一个single call stack。一个时间段中,call stack只能作一件事。一个时间段中,程序只能执行一片代码片断,这就是所谓的“single threaded”的意思。“single threaded”,“single call stack”和“do one thing at a time”这三者的关系以下:
single threaded === single call stack === do one thing at a time
复制代码
如今,让咱们开始尝试经过可视化将咱们脑海里面零散理解组织起来。若是咱们有大家左边的代码(演讲者指向屏幕的ppt):
function multiply(a,b){
return a * b;
}
function square(n) {
return multiply(n,n);
}
function printSquare(n) {
var squared = square(n);
console.log(squared);
}
printSquare(4);
复制代码
这里面一个叫multiply的函数,负责将两个数字相乘。一个叫square的函数,负责调用multiply来实现一个数的平方。而后有一个叫printSquare的函数负责将调用square的结果用console.log方法打印出来。最后,咱们在文件的底部开始调用print函数。
这些代码应该没写错吧?理解起来没问题吧?嗯,那咱们开始把它跑起来。咱们上一个ppt。能够这么说,call stack基本上是一种用来告诉咱们当前是执行到程序的哪里的数据结构。若是咱们执行到一个函数调用,那么咱们就把一些东西放到栈顶,若是咱们从一个函数里面return出去,那么咱们就把栈顶的那些东西pop出来。这就是call stack能作的事情。当你运行这个文件的时候,我能够把这个文件自己看成是整个程序的main函数。因此,一开始,咱们把main函数放进去。从文件的顶部开始看,接下来是一些函数声明,they are just like defining the state of the world。最后,咱们来到了printSquare函数的调用。一提到函数调用,咱们得立刻把它push 到call stack的顶部。而在执行printSquare函数的过程当中,首先遇到了square函数调用,因此,咱们立刻又把square函数push到call stack的顶部。而在执行square过程当中,它又调用multiply函数。与此类推,咱们又把multiply函数push进去。如今call stack顶部的是multiply函数,那么咱们就先执行它。它对A和B执行乘法操做后,就把结果return出去。一旦咱们遇到return语句,咱们就把call stack顶部的函数pop出来。因此,咱们把multiply函数pop走,如今程序返回到square函数,由于遇到了return语句,与此类推,咱们把square从call stack顶部pop走,回到了printSquare函数。最后,咱们调用console.log把结果打印出来。很明显,这里已经没有return了,咱们到了函数的底部了。经过这种可视化的方式来说解,不知道大家明白了没?(yes,Phil(译者注:演讲者本身用女生的声明模仿观众说了这话))。即便你以前脑海里面没有call stack这个清晰的概念,可是若是你作过浏览器端的开发的话,那么你已经遇到过它了。在举个例子,若是咱们有一下代码:
function Foo(){
throw new Error('Oops');
}
function bar(){
Foo()
}
function baz(){
bar()
}
baz()
复制代码
若是咱们把以上代码在Chrome浏览上运行的话,咱们在console就会看到打印出来的stack trace。是的,stack trace就是执行出错的时候call stack的当前状态。
从上往下看,咱们依此看到:
unaugth error: Oops
Foo
bar
baz
anonymous
复制代码
最后的那个anonymous function就是咱们的main函数。
一样的,你也许听过这样的术语“blowing the stack”,那下面就是一个例子:
function Foo(){
Foo()
}
复制代码
在这个例子当中,main函数调用了Foo函数,而后在Foo函数又调用了Foo函数,与此类推。当咱们执行这段代码的时候,chrome会说,递归调用Foo函数16000次可能不是你的本意,我如今报个错,杀死掉进程,好让你定位到bug的所在。
因此,虽然我可能会让你看到了call stack的新的一面,可是其实在实际的开发中你多少对它已经有点印象了。
咱们讲完了call stack。接下来的大主题就是blocking(阻塞)。咱们将会讨论什么是blocking和blocking的行为表现是如何的。
实际上,对于blocking和非blocking这两个概念并无十分严格的定义。在我看来,blocking就是指执行得十分缓慢的代码而已。举个例子,console.log不慢,可是若是执行一个1到100亿的循环后去作console.log,那么咱们能够说这个console.log是很慢的。网络请求是慢的,图片请求也是慢的。当一个被push进call stack的函数执行起来很慢的话,咱们就能够说这个函数是blocking的。 这就是咱们平时提到的blocking的实际含义。在这里,咱们有一个用伪代码写成的小例子。
var foo = $.getSync('//foo.com');
var bar = $.getSync('//bar.com');
var qux = $.getSync('//qux.com');
console.log(foo);
console.log(bar);
console.log(qux);
复制代码
里面有个jQuery AJAX请求风格的getSync方法。若是这些getSync方法执行起来都是同步的,那么会发生什么呢?咱们暂时先忘记异步callback其实本质也是同步的这个事实。按照咱们的写法,执行起来的结果将会是这样的:咱们调用了getSync方法,而后接下里就是等待。由于咱们正在发起了网络请求,而网络请求是于计算机硬件相关联的,它们每每是很慢的。好在,咱们的第一个网络请求终于完成了。咱们接着发起第二个网络请求,而后咱们接下来仍是等待。第二个网络请求完了,咱们立刻发起第三个......与此类推。只要其中的一个网络请求永远都不会完成的话,那么我以为我如今能够回家洗洗睡了。最终,这三个网络请求的blocking行为完成了,call stack得以清空掉,是吧?因此,若是在一个single threaded的编程语言里面,你是不能像ruby那样使用线程的话,那么,就会出现像这个例子所演示的情形那样-咱们发起了一个网络请求,在它完成以前,咱们只有干等待。除此以外,咱们机关用尽啊。为何咱们将这种结果看成一个问题而存在呢?一切的一切都是由于咱们的代码跑在浏览器端。
下面我再举一个例子进行说明。
做为开发者,若是咱们想用户看到一个好看的,流畅的界面的话,那么咱们就不能阻塞call stack。咱们应该怎么去解决这个问题呢?好吧,最简单的解决方案就是异步callback方案。在浏览器中,几乎没有哪一个函数是blocking的,在nodejs里面也是同样的。全部会阻塞call stack的函数都会被改为异步的。“将函数改为异步”基本是是这样的模式:咱们须要执行某些代码,与此同时,咱们会提供一个callback给当前的运行时。当须要执行的代码完成了,运行时就会执行咱们提供的callback。若是你见过javascript,那么你也就见过了异步callback的样子了。
下面是一个简单的例子:
console.log('hi');
setTimeout(function(){
console.log('there');
},5000);
console.log('JSConfEU');
复制代码
咱们用这个简单的例子提醒一下你们咱们讲到哪里。咱们console.log "hi",而后执行setTimeout。可是这个setTimeout的执行会把console.log入队到将来某个时刻去执行。咱们跳过这个log,转而去log“JSConfEU”。5秒钟以后,咱们会看到一个“there”的log,是吧?这个过程没问题吧?不错。基本上,咱们都知道这就是setTimeout要作的事情。显然,异步callback是跟咱们以前见到的call stack有关的。那么是如何相关法呢?咱们先把代码跑起来。首先console.log('hi')入栈,而后是setTimeout。咱们知道这个setTimeout是不会立刻执行的,而是会在5秒钟以后才执行。咱们不能把它推入到栈中。由于咱们目前尚未找到恰当的方式去描述它,因此就暂时假设它平白无故消失了。固然,咱们在后面会回来说它的。setTimeout平白无故消失后,咱们就将console.log('JSConfEU')入栈,而后log“JSConfEU”。最后,call stack被清空了。5秒钟以后,console.log('there')神奇地出如今call stack上面了。这是怎么回事呢?这时候该是event loop和concurrency出场的时候了。是的,我已经反反复复跟你说,javascript在一个时间段里面只能作一件事件。打个比方,你不能在执行其它代码的时候让它(译者注:它指的是javascript运行时)发起一个AJAX请求。你不能在执行其它代码的时候执行setTimeout。而咱们之因此能以并行的方式去作事情,那是由于浏览器的能力远在javascript运行时之上(译者注:浏览器是javascript运行时的超集,javascript运行时以外还有别的东西)。记住下面这种图:
跟以前演示的那样,执行console.log“hi”,把“hi”打印到控制台,接下来,来看看当咱们调用setTimeout的时候到底发生了什么。咱们把一个callback函数和须要延迟的毫秒数传入到setTimeout调用中。如今,对于咱们来讲,setTimeoout就是一个由浏览器提供给咱们的API。它的实现源码并不在v8的源码里面。这是正在运行中的javascript运行时以外的东西。浏览器会另外给你设定一个倒计时。浏览器中的这部分代码负责为你倒数时间。这也就是说,setTimeout调用已经完成了,咱们二话不说就把它从call stack中pop出去。接着是log“JSConfEU”。咱们已经在webAPI中启动了一个5秒中倒计时。
如今,webAPI不能直接修改你的代码。它不能在本身准备好的时候直接将一些代码块推入到call stack中。若是它真的这么干的话,这些代码块就会随机地插入到你当前代码的中间,这岂不是乱套了。到了这里,是时候让task queue或者说callback queue(译者注:以后统一采用“task queue”的叫法)参与进来了。任何一个web API一旦它完成了本身的任务后,就会把相关联的callback推入到task queue中。最终的最终,咱们终于凑齐了讲解了event loop,本次演讲的题目:“what the heck is the event loop?”的全部要素了。
那到底什么是event loop呢?event loop就好像是whole equation(整个方程?)中的一个最简单的小片断。它有一个简单的任务。那就是同时监视call stack和task queue。若是call stack当前处于清空状态的话,那么event loop就会把task queue的排在第一个东西(callback)pop出来,推入到call stack中去运行。 回归到这个演示中来。咱们看到当前call stack是为空的,并且有个一callback在task queue上。看到本身来活了,event loop立刻就运做起来的。它把这个callback推入到call stack中。时刻记住,call stack是javascript的地盘,它背靠v8这颗大树。它的地盘它作主。因而乎,这个刚出现的callback立刻就被执行了-console.log('there')被执行了,在控制台,“there”被打印出来。到这,call stack被清空了,咱们的代码不折不扣被执行完了。你们理解了没呢?everyone,where me?(译者注:意思是你们都没睡着吧,看到我吗?)完美!
好的,咱们已经看到了event loop的基本工做流程了。在你跟异步编程打交道的过程当中,你遇到的众多情景中的其中一个是这样的:人家不跟你说明到底啥缘由,就是让你用0毫秒去调用setTimeout。这时候你可能内心在想:“稍等,你让我在0毫秒后执行这个函数。我为何要把个人代码包裹在一个0毫米的setTimeout里面呢?这样作意义何在呢?”。当你第一次遇到这种状况的时候,应该跟我同样,确定很困惑。咱们都知道这种写法确定是作了什么,可是不知道其中的具体缘由。这里也不卖关子了,具体缘由就是人们想要把代码延迟到call stack清空后才执行。那么下面咱们来演示一下用0毫秒去调用setTimeout的状况。若是你写过javascript代码,你就会知道跟上面的例子(非0毫秒去调用setTimeout)的结果是同样的。 咱们将会看到依次打印“hi”和“JSConfEU”,“there”将会在最后打印。下面咱们看看具体的演示。在call stack以0毫秒去调用setTimeout,setTimeout立刻就会从call stack中pop走。接着它的callback会立刻被webAPIpush到task queue中。请记住我说过关于event loop的话。event loop必须等到当前call stack清空以后才能把task queue中的callback推入到call stack去的。因此,当前没有清空的call stack会继续执行。因而乎,咱们先看到打印"hi",而后看到打印“JSConfEU”,此时call stack已经清空了,是时候event loop参与进来的,最后调用了你的callback。无论基于何总缘由,咱们均可以把代码的执行延迟到call stack最后一帧或者等到call stack清空后再执行。上面提到的以0毫秒去调用setTimeout只是这种作法的一个示例而已。全部的webAPI都是以相同的原理在工做。如今假设咱们要准备着callback向一个URL发起一个AJAX请求。这代码的执行原理跟上面提到的setTimeout都是同样的。oops,对不起。console打印“hi”,而后浏览器使用webAPI发起了AJAX请求。记住,真正实现发起AJAX请求的源代码不在javascript的运行时中,而是在浏览器对webAPI的实现源码里面。因此,咱们保存着allback,把小菊花转起来,默默等待。而后,继续执行咱们在call stack中代码。直到AJAX请求完成或者永远都完成不了。如今我们假设这个AJAX请求完成了,那么它对应的callback会立刻被推入到task queue中,由于此时call stack清空了,因此,它被event loop选中了,推入到call stack中跑起来了。javascript的异步调用背后所发生的事情大概就是这么多了。
下面,让咱们来跑一个疯狂的,复杂的示例吧。我但愿这段代码可以跑起来。可能大家不知道吧,此次演讲的全部PPT我都是在keynote上完成了。单单是当前这张幻灯片就有大概500个动画步骤(代码爆炸,化为灰烬,观众大笑)。
译者注:外国友人不多会把PPT称为“PPT”,正统的叫法应该“Slides(片子)”或是“Deck”,前者意指单张或几张PPT页,后者意指一整套PPT报告(就像a deck of cards)。
哇,真糸得意。我在这里给出个连接。嗯....大伙们,我放得够大吗?你们能看见吗?好的。基本此次演讲思路和PPT都是沿用了今年初Scotlan JS上的东西。在那次演讲以后,我损坏了大半部分的PPT。而后,本身又没有耐心去重作那些PPT。由于用keynote作PPT真的是一件(坐到)屁股疼的事情。因此,我选择了一条捷径。我本身写了一个工具用来可视化javascript的运行时机制。这个工具叫作loop。下面,咱们用这个新工具把这个例子跑起来吧。
console.log('Started');
$.on('button', 'click', function onClick(){
console.log('Clicked');
});
setTimeout(function onTimeout(){
console.log('Timeout finished');
}, 5000);
console.log('Done');
复制代码
这个例子基本上跟前面的例子差很少,只不过我没有使用shim过的XHR。使用shim过的XHR来演示彻底可行,只不过我这里没有这么干而已。正如你所看到的那样,咱们的代码是就是为了log点东西出来。首先,$.on()是对addEventListener的一个封装,而后是以5000毫秒调用setTimeout。最后,log一个“Done”。下面咱们真正把代码跑起来,看看到底发生了什么。
代码开始后,打印个“Started”后,刚入栈的$.on()和setTimeout都会立刻被pop出去,而后加入到webAPI的地盘中去。call stack中的代码继续执行。5000毫秒到了,咱们就把callback推入到task queue中去。此时call stack处于清空状态,因此callback会被推入到call stack去执行。最后打印出“Timeout finished”,代码执行完毕。若是我点击一下页面的按钮,那么浏览器就会触发webAPI的执行,把点击事件的callback推入到task queue中去。call stack为空,而后把callback推入到call stack中执行。若是咱们连续点击100屡次,咱们能看到会发生什么。在我点击按钮以后,click的callback不会被立刻执行,而是推入到task queue。当event loop开始安排task queue的时候,click的callback才能被处理到。是吧?我还有好几个例子。咱们将经过这些例子来谈谈你跟异步API打交道过程可能遇到的可是没有去深究的东西。我将会使用这个loupe的工具来进行演示。
首先第一个例子是以1秒的延迟调用setTimeout四次,每次都是在1秒后打印“hi”:
setTimeout(function onTimeout(){
console.log('hi');
}, 1000);
setTimeout(function onTimeout(){
console.log('hi');
}, 1000);
setTimeout(function onTimeout(){
console.log('hi');
}, 1000);
setTimeout(function onTimeout(){
console.log('hi');
}, 1000);
复制代码
等到全部的callback被入队到task queue之时,没有任何一个callback被执行。此时,第四个callback尚未执行,时间已经超出了它所要求延迟的1秒了。是吧。这个例子说明的是javascript中的time out的实质含义。咱们传入延迟时间(以毫秒为单位)表明的正是可执行的最小时间,而不是确切的时间。浏览器并不能保证在你传入的延迟时间内执行你的callback。以0毫秒调用setTimeout也是同样的。你的callback不会立刻,当即被执行,而是被承诺为尽快地执行。是吧。
下面这个例子,我想来谈论一下callback。callback这个概念的定义取决于跟谁说和它们如何被解析的。callback这个概念能够有两种定义。第一种是,凡是被别的函数所调用的函数均可以称之为callback;第二种是,更加明确地指那种提供给异步操做的,最终会被推入到task queue的函数。下面这一点代码会演示一下着二者的不一样:
// 同步版本
[1,2,3,4].forEach(function(i){
console.log(i);
});
// 异步版本
function asyncForEach(array, cb){
array.forEach(function(i){
setTimeout(cb.bind(null,i), 0);
})
};
asyncForEach([1,2,3,4],function(i){
console.log(i);
});
复制代码
对于同步版本的代码,咱们在数组上调用了forEach方法,forEach接受一个函数做为参数,尽管这个函数不是被异步执行的,可是它是跑在当前的call stack中,你也能够称这个函数为callback。我将会运行这段示例代码,让咱们看看这二者之间的差别究竟是什么。首先,同步版本的代码先运行了。整个代码块将会推入到call stack中,它就在这里占用并阻塞着call stack,是吧?直到当前call stack清空,才轮到异步版本的代码执行。是的,执行速度降下来了。实际上,咱们将好几个callback推入到了task queue中了。等到当前call stack清空了,咱们才能依次地从task queue中把callback弹出来,推入到call stack 中运行,最终打印出所遍历的元素。在这个例子中,console.log()的执行足够快,因此使用异步方式来遍历数组的好处并无体现出来。假设,你如今在遍历数组的过程当中对数组的元素作一些比较耗时的操做的时候,那么,异步方式的写法的好处就会体现出来。我好像有在哪里写过这种例子,噢,不,我应该记错了,我没有这样的例子。好吧,咱们改造一下上面这个例子来进行说明:
function delay() {
// just do the slow thing
}
// 同步版本
[1,2,3,4].forEach(function(i){
console.log("Processing sync");
delay();
});
// 异步版本
function asyncForEach(array, cb){
array.forEach(function(i){
setTimeout(cb.bind(null,i), 0);
})
};
asyncForEach([1,2,3,4],function(i){
console.log("Processing async");
delay();
});
复制代码
好的,如今我打算打开一个模拟浏览器repaint或者说render的开关。这个开关是我这个早上比较匆忙整合都这个loop工具的。目前,我没有提到的一点是全部的这一切(call stack, webAPI,task queue和event loop)是如何界面渲染打交道的。好吧,我貌似提到过,可是没有去深刻解释它。能够这么说,浏览器的运行是受你当前正在执行的javascript代码所约束的。理想状态下,浏览器想要以1秒60帧的速度(也就是花16毫秒去完成一帧的渲染)去渲染屏幕的。60FPS是浏览器所能达到的刷新界面的最快的速度了。这里再次强调,60FPS这种理想状态是受正在执行的javascript代码所约束的。若是当前call stack不为空的话,实际上浏览器是没法进行界面重绘的。 你能够把界面渲染理解为一个有callback与之对应的异步操做。这种render callback也是须要等待call stack清空以后才能执行的。render callback与普通的入队到task queue的callback相比,不一样之处在于,render callback比普通的callback的优先级要高。每隔16毫秒,浏览器就会往render queue上入队一个render callback。这些render callback必须等到当前的call stack清空才能被执行。这里所提到的“render queue”是由于要解释界面渲染机制而模拟出来的。这就好像,每隔1秒,render queue就会问浏览器:“我能够作界面渲染了吗?”。浏览器回答:“是的,你能够。”,而后又再下一秒进行如此类推的对话。
下面就是一个由于javascript代码没有获得很好编排而致使界面变慢的例子。这是一个处理scroll事件的例子。
最后浏览器都必须处理一个个地处理这些callback。每一个callback执行起来都是挺慢的。这个时候,你不是阻塞call stack,你是直接用callback“淹没”了task queue。这种状况跟将触发大量callback背后的状况进行可视化很像。固然,经过debounce来减小callback的执行从而增长call stack的空闲时间,这种方式也是可行的。可是,我采用的另一种方式。我将这些event callback全数推入task queue,可是咱们每隔几秒钟或者等用户中止滚动后的一小段时间后就作一些耗时的操做,这种方式也是可行的。我会有另一个专门来说述这个可视化loop工具的实现原理的演讲。这实现原理大致是这样的:当代码运行在运行时的时候,我经过使用一个叫Esprima的javascript parser来执行这段代码来下降代码的执行速度。具体经过往里面插入一段耗时半秒中的大while循环来达到的。把Esprima放在了web worker上,同时在上面完成了可视化这段代码执行流程所须要作的全部事情。个人那个完整的演讲将会深刻探究其中细节。对于此次演讲的到来,我感到十分的兴奋。到那个时候,我将会跟你们好好唠唠这事,届时就功德圆满了。好吧,就这样。谢谢你们。
single threaded === single call stack === do one thing at a time
复制代码
关于“blocking”的理解: 对于blocking和非blocking这两个概念并无十分严格的定义。在我看来,blocking就是指执行得十分缓慢的代码而已。
javascript中的time out(setTimeout/setInterval)的实质含义是 “可执行的最小时间”,而不是“确切的时间”。
callback这个概念能够有两种定义。第一种是,凡是被别的函数所调用的函数均可以称之为callback;第二种是,更加明确地指那种提供给异步操做的,最终会被推入到task queue的函数。
浏览器做为一个界面终端而存在,这就决定了创造一个流畅的界面浏览体验是其天生的职责之所在。创造一个流畅的界面浏览体验具体是是指尽量地以60FPS的速率去刷新界面。而为尽量地以60FPS的速率去刷新界面,那么咱们就要尽量地不要长时间占用call stack。为了避免要长时间占用call stack,那么咱们必须学会编排咱们执行耗时较长的代码。具体来讲,就是把执行耗时较长的代码改成异步代码,push到task queue中。这样子,虽然render callback跟普通的callback是交替执行,可是相比于同步方式,界面可以获得尽快的刷新,故浏览体验大胜一筹。