事件的处理
浏览器是一(yī)个(gè)事件驱动(event-driven)架构的软件。它的UI线程中会不断产生(shēng)用户(hù)事(shì)件。但是(shì)处理事件的JavaScript是(shì)单(dān)线程执行的,这是(shì)一个浏览器环境下(xià)难以改(gǎi)变的现状(HTML5 Web Works没(méi)有从本质上(shàng)改(gǎi)变这个模型)。这意(yì)味着:在(zài)JavaScript处(chù)理(lǐ)某个任务(执行(háng)某(mǒu)段代码(mǎ))过程(chéng)中(zhōng),如果产生了用户事件,它不会立即被处理(lǐ)。那(nà)这种情况该(gāi)怎么(me)办(bàn)呢?
浏览器维护了一个(gè)“任务队列”(一个(gè)优先队列数据结构(gòu)),它(tā)是一个浏览器进程资源。每当UI线程产生(shēng)一个事件,事件对象就被当做(zuò)任务放入(rù)任务队列中(enqueue)。当JavaScript执行线程空闲的时(shí)候,队列中的(de)一个任务就会(huì)被送往JavaScript执(zhí)行线程(chéng)(dequeue),进(jìn)行相应(yīng)的处(chù)理。这个enqueue和dequeue的机制就是“Event Loop”。 但是,不仅用户(hù)事(shì)件可以被Event Loop机制(zhì)处(chù)理,还能更多的东西是依赖这(zhè)个机制的。
异步IO的(de)处理(lǐ)
如(rú)果没有异步(bù)的(de)理念,这个世界会完(wán)全不同(tóng):一个耗时的I/O操作(例(lì)如HTTP请(qǐng)求)会导致JavaScript执行线程等待,而后续的操作得(dé)不到执行。这种情况下(xià),一个(gè)耗时的服务(wù)器端数据(jù)库(kù)操作http请求,会让JavaScript执行线(xiàn)程阻塞,浏览器将长期处于假死状态,在此期间,其他后续操作(zuò)(包括用户的交(jiāo)互事(shì)件)得不到响应。
好(hǎo)在浏览器不是单线程的。它(tā)可(kě)以(yǐ)(但不是(shì)必须)让(ràng)这些(xiē)I/O任务让其他(tā)线程来托管,这样就形成了一个(gè)执行任务(wù)的线程池(chí)。但是(shì)这些任(rèn)务(wù)的结果总归要回到(dào)JavaScript执行线(xiàn)程上(shàng)处理,于是这些(xiē)任务(wù)也(yě)被(bèi)放到任务队列中:需要被托(tuō)管的任务被放入(rù)队列中(enqueue),已完成(chéng)的任务(wù)会(huì)被从队列中一个(gè)个取出(dequeue),回到JavaScript执行线程执行回调。在这些耗时的I/O任务(wù)被托管的(de)时候,JavaScript执行线程可以执(zhí)行其他代码。在Node中,这个过程是类似的。本(běn)文不表。 这便是异步的原(yuán)理了。我们(men)看到(dào)它(tā)同样依(yī)赖Event Loop的(de)机制。
定时器
浏(liú)览器的全局对象window提(tí)供了(le)两个方法(fǎ),setTimeout和setInterval。这两个方法其实是调用了浏(liú)览器的API,将一个任务移除出JavaScript执行线(xiàn)程中,延时处理。我们现在(zài)马上可以反应过来:这个将要(yào)被(bèi)延时的任务(wù)同样是放到了任务队列中。在一次Event Loop过程(chéng)中(zhōng),它会优先将该时间(jiān)点下已经到(dào)时的延时任务移除出(chū)队列,放入(rù)JavaScript执行(háng)线程中。这意味(wèi)着(zhe),任务队列是一个优先队列。 但是由于(yú)JavaScript执行线程的(de)执行时间是(shì)不确定的,所(suǒ)以这个延时只是一个(gè)大体(tǐ)的值,它取决于JavaScript执行线(xiàn)程(chéng)的执行时间。
回调函数(shù)
任务完成的时候,JavaScript需(xū)要执行哪段代码来处理呢(ne)?当然是回(huí)调(diào)函数了。 但是不免奇怪(guài)的一点就是:JavaScript中怎么(me)知道(dào)要执行的是哪个回(huí)调函数呢?答案(àn)就(jiù)是(shì):任务被放入任(rèn)务队列的(de)时(shí)候(hòu),该任务的回调函数(shù)会被注册(cè)(注(zhù)册到什么地方?需要(yào)进一步探究)。这(zhè)样,当特定任务完成的时候,任务结果和回(huí)调(diào)标记(jì)会返回给JavaScript执(zhí)行线程,进入执(zhí)行栈(zhàn)。
事件处(chù)理器(qì)
与其他(tā)任务不同,事件并不是由JavaScript执行(háng)线程发出的,而是从UI线程中发(fā)出的(de)。 事件处理器(qì)和回调函数(shù)类(lèi)似。但是特定的事件(jiàn)处理器(qì)在浏(liú)览器进入异步事(shì)件驱动阶段时就(jiù)会针对(duì)特定(dìng)的事件注册。当事件对(duì)象返回到JavaScript执行(háng)线程时,事件处理器(qì)也(yě)会同时进入执行(háng)栈中执行。
——本文并非(fēi)原创,如有侵权请(qǐng)联系管理(lǐ)员删除。