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