JavaScript的事件执行机制

由于javascript是单线程的,只能在JS引擎的主线程上运行的,所以js代码只能一行一行的执行,不能在同一时间执行多个js代码任务,这就导致如果有一段耗时较长的计算,或者是一个ajax请求等IO操作,如果没有异步的存在,就会出现用户长时间等待,并且由于当前任务还未完成,所以这时候所有的其他操作都会无响应。

js最开始只是为了处理一些表单验证和DOM操作而被创造出来的,所以主要为了语言的轻量和简单采用了单线程的模式。多线程模型相比单线程要复杂很多,比如多线程需要处理线程间资源的共享问题,还要解决状态同步等问题。

JavaScript的事件执行机制:

当JS解析执行时,会被引擎分为两类任务,同步任务(synchronous) 和 异步任务(asynchronous)。对于同步任务来说,会被推到执行栈按顺序去执行这些任务。对于异步任务来说,当其可以被执行时,会被放到一个 任务队列(task queue) 里等待JS引擎去执行。当执行栈中的所有同步任务完成后,JS引擎才会去任务队列里查看是否有任务存在,并将任务放到执行栈中去执行,执行完了又会去任务队列里查看是否有已经可以执行的任务。这种循环检查的机制,就叫做事件循环(Event Loop)任务队列也被分为 微任务(microtask)队列 & 宏任务(macrotask)队列。

Event Loop的完整执行顺序是:

首先执行执行栈里的任务。

执行栈清空后,检查微任务(microtask)队列,将可执行的微任务全部执行。

取宏任务(macrotask)队列中的第一项执行。

回到第二步。

注意: 微任务队列每次全执行,宏任务队列每次只取一项执行。

setTimeout(() => {
    console.log('我是第一个宏任务');
    Promise.resolve().then(() => {
        console.log('我是第一个宏任务里的第一个微任务');
    });
    Promise.resolve().then(() => {
        console.log('我是第一个宏任务里的第二个微任务');
    });
}, 0);

setTimeout(() => {
    console.log('我是第二个宏任务');
}, 0);

Promise.resolve().then(() => {
    console.log('我是第一个微任务');
});

console.log('执行同步任务');

最后的执行结果是:

// 执行同步任务
// 我是第一个微任务
// 我是第一个宏任务
// 我是第一个宏任务里的第一个微任务
// 我是第一个宏任务里的第二个微任务
// 我是第二个宏任务

常见的异步模式:回调函数;事件监听;发布/订阅模式(又称观察者模式);promise;Generator函数;ES7中async/await。

回调函数:

回调函数是异步操作最基本的方法,比如有一个异步操作(asyncFn),和一个同步操作(normalFn)。如果按照正常的JS处理机制来说,同步操作一定发生在异步之前。如下:

function asyncFn() {
    setTimeout(() => {
        console.log('asyncFn');
    }, 0)
}

function normalFn() {
    console.log('normalFn');
}

asyncFn();
normalFn();

// normalFn
// asyncFn

如果我想要将顺序改变,可以使用回调的方式处理:

function asyncFn(callback) {
    setTimeout(() => {
        console.log('asyncFn');
        callback();
    }, 0)
}

function normalFn() {
    console.log('normalFn');
}

asyncFn(normalFn);

// asyncFn
// normalFn

事件监听:这是一种事件驱动模式,异步任务的执行不取决于代码的顺序,而取决于某个事件是否发生。比如通过点击按钮或者trigger的方式触发这个事件。

发布/订阅模式(又称观察者模式):

猜你喜欢

转载自www.cnblogs.com/xjy20170907/p/11426170.html