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