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