Javascript事件轮询

这篇文章关于什么?JAVAscript作为浏览器脚本语言 , 已经逐渐变得无处不在 , 它让你对事件驱动模型有了基本理解 , 以及它与request-response模型的典型语言 , 如Ruby , Python和Java的区别 , 我将阐述一些关于JavaScript一致的核心概念 , 包括它的事件轮询和消息队列 , 希望能帮助你理解这门或许你不是完全理解的语言 。
致读者:这篇文章写给准备使用Javascript进行服务端/客户端开发的web开发工作者(或者准备从事该职业者) , 如果你对事件轮询机制有比较好的了解 , 这篇文章或许你会觉得很熟悉 。对于不是很了解事件轮询机制的读者 , 我希望能帮助你对这每天读写的代码有一个基本的理解 。
非阻塞I/O在Javascript中 , 几乎所有的I/O都是非阻塞的 , 其中包括http请求 , 数据操作以及磁盘读写;单线程询问任务运行时机以及执行任务 , 通过使用回调函数 , 能让Javascript线程在回调完成之前执行其它任务 。当一个执行完成的时候 , 会去执行由回调函数提供的有序消息队列中的下一个任务 。
当开发者熟悉了这种交互模式 , 用户习惯了这种界面 — 当事件发生 , 例如“mousedown” , “click”这种随时可能被触发的事件 , 它不同于同步机制 , 请求-响应模型很少用在服务端应用上 。
让我们比较两块代码 , 它们分别发起HTTP请求于www.google.com然后在控制台输出响应结果 。首先 , Ruby , 使用Faraday:
response = Faraday.get 'http://www.google.com'puts responseputs 'Done!'执行结果如下所示:
get方法已执行 , 然而该线程直到有响应才被回收
该来自Google的响应返回的数据并没有存在变量中
响应结果输出在控制台中
直至最后 , “任务完成”才出现在控制台中
让我们用Javascript的Node.js及它的Request库来做同样的事:
request('http://www.google.com', function(error, response, body) { console.log(body);});console.log('Done!');请求已被执行 , 在请求得到响应之前则已跳过一个匿名回调函数(该函数并未执行)
“任务完成”马上出现在控制台中
一段时间之后 , 收到响应 , 此时回调函数才被执行—在控制台输出响应结果
事件轮询非耦合机制使得Javascript线程能在等待异步操作完成及其回调函数执行之前执行其它任务 。那么 , 在内存中在哪激活回调?回调按什么规则执行?什么会让回调执行呢?
Javascript线程包括一个储存了待执行任务的消息列表的消息队列 , 以及与它们相关联的回调函数 , 这些消息按照它们的响应顺序排列(例如鼠标点击 , 或者收到来自HTTP请求的响应)每条消息都有回调函数 , 如果没有提供回调函数:例如当用户点击一个按钮但是没有提供回调函数 , 则没有消息会被添加到消息队列 。
在每一次轮询中 , 任务队列会记录下一条消息(每次记录会返回一个“tick”) , 当轮询到这条消息时 , 该消息所对应的回调函数则被执行 。

Javascript事件轮询

文章插图
 
在最初的架构中 , 回调函数通过调用栈来实现 , 由于Javascript是单线程的 , 消息队列是阻塞的 , 对于后续任务 , 必须等待之前的任务返回栈中所有回调函数 , 才能将新任务的回调函数加入到栈中 。在随后的架构中加入了函数(同步的)对栈的新的调用方法(此处例举一个初始化为changeColor的函数) 。
function init() { var link = document.getElementById("foo"); link.addEventListener("click", function changeColor() { this.style.color = "burlywood"; });}init();在这个例子中 , 当用户点击“foo”这个元素 , “onclick”事件被触发 , 一个消息(以及回调函数changeColor)加入消息队列 。当队列按序执行到该消息时 , 它的回调函数changeColor被唤起 。
当回调函数changeColor返回(或者出错被丢掉) , 事件轮询继续执行 。只要与”foo”元素的onclick事件绑定的回调函数changeColor存在 , 随后在该元素上的click事件都会使得更多的消息(及其回调函数changeColor)被加入队列 。


推荐阅读