乐动网页版-乐动(中国)

  • 乐动网页版-乐动(中国)

    浅谈javascript事件循环机制

    发(fā)布于(yú): 2018-07-06    浏览(lǎn): 2268    作者:Wu


    事(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ú)。

    在线(xiàn)客服

    乐动网页版-乐动(中国)

    乐动网页版-乐动(中国)