乐趣区

关于javascript:事件循环Event-Loop

1. 对于 javascript


   javascript 是一门单线程语言,在最新的 HTML5 中提出了 Web-Worker,但 javascript 是单线程这一外围仍未扭转。所以所有 javascript 版的 "多线程" 都是用单线程模仿进去的,所有 javascript 多线程都是纸老虎!(不论是什么新框架新语法糖实现的所谓异步,其实都是用同步的办法去模仿的)

2.javascript 事件循环

事件循环是 js 实现异步的一种办法,也是 js 的执行机制。首先浏览器会把主工作队列中的同步工作挨个全副执行完,而后再去期待工作队列中看哪个工作能够执行了,而后把该执行的工作放到主工作队列中去执行,等这个工作执行完,再去期待工作中看谁能够执行了,再把这个工作放到主工作队列中执行... 如此循环。这种循环叫做事件循环(Event Loop)

js 是单线程,js 工作也要一个一个程序执行。如果一个工作耗时过长,那么后一个工作也必须等着。那么问题来了,如果咱们想浏览新闻,然而新闻蕴含的超清图片加载很慢,难道咱们的网页要始终卡着直到图片齐全显示进去?因而聪慧的程序员将工作分为两类:1)同步工作 2)异步工作

一张图示意事件循环

#1. 同步和异步工作别离进入不同的执行 "场合",同步的进入主线程,异步的进入 Event Table 并注册函数。#2. 当指定的事件实现时,Event Table 会将这个函数移入 Event Queue。#3. 主线程内的工作执行结束为空,会去 Event Queue 读取对应的函数,进入主线程执行。#4. 上述过程会一直反复,也就是常说的 Event Loop(事件循环)。主线程执行栈何时为空?js 引擎存在 monitoring process 过程,会继续一直的查看主线程执行栈是否为空,一旦为空,就会去 Event Queue 那里查看是否有期待被调用的函数。~~~~

所以能够看做是这样的:
1. 浏览器线程先执行同步工作,途中遇到异步工作就将其退出到期待工作队列中去,而后持续向下执行,
2. 等同步工作全副执行结束后,再去期待工作队列中去将所有可执行的微工作一一执行,
3. 执行完微工作后在拿取第一个先达到执行条件的宏工作来执行,
4. 执行完后再去期待工作队列中清理执行完所有已达到执行条件的微工作,
5. 而后再拿取下一个宏工作来执行,如果宏工作执行产生微工作或者微工作执行产生宏工作就一样退出到期待工作队列中,而后还是依照主线程每次到期待队列中先执行完所以的微工作再一一执行宏工作的程序来走

异步工作都是谁先达到条件谁先执行,然而谁先达到执行条件也有优先级的问题,这个优先级要看这个工作是宏工作还是微工作;微工作的优先级比宏工作的要高;

3. 运行机制

在事件循环中,每进行一次循环操作称为 tick,每一次 tick 的工作解决模型是比较复杂的,但关键步骤如下:

*     执行一个宏工作(栈中没有就从事件队列中获取)*     执行过程中如果遇到微工作,就将它增加到微工作的工作队列中
*     宏工作执行结束后,立刻执行以后微工作队列中的所有微工作(顺次执行)*     以后宏工作执行结束,开始查看渲染,而后 GUI 线程接管渲染
*     渲染结束后,JS 线程持续接管,开始下一个宏工作(从事件队列中获取)

一张图解释

宏工作:
(macro)task,能够了解是每次执行栈执行的代码就是一个宏工作(包含每次从事件队列中获取一个事件回调并放到执行栈中执行)。浏览器为了可能使得 JS 外部(macro)task 与 DOM 工作可能有序的执行,会在一个(macro)task 执行完结后,在下一个(macro)task 执行开始前,对页面进行从新渲染,流程如下:(macro)task-> 渲染 ->(macro)task->...
微工作:
microtask, 能够了解是在以后 task 执行完结后立刻执行的工作。也就是说,在以后 task 工作后,下一个 task 之前,在渲染之前。所以它的响应速度相比 setTimeout(setTimeout 是 task)会更快,因为无需等渲染。也就是说,在某一个 macrotask 执行完后,就会将在它执行期间产生的所有 microtask 都执行结束(在渲染前)。
宏工作(macrotask) 微工作(microtask)
谁发动的 宿主(Node、浏览器) JS 引擎
具体事件 1.script(同步代码), 2.setTimeout/setInterval, 3.UI rendering/UI 事件, 4.postMessage,MessageChannel, 5.setImmediate,I/O(Node.js) 1. Promise, 2.MutaionObserver, 3.Object.observe(已废除;Proxy 对象代替), 4.process.nextTick(Node.js)
谁先运行 后运行 先运行
会触发新一轮 tick 吗 不会

代码块 1

setTimeout(function(){console.log('1')
});
new  Promise(function(resolve){console.log('2');
  resolve();}).then(function(){console.log('3')
});
console.log('4');//2  4  3  1

剖析:
(1)settimeout 是宏工作,尽管先执行的他,然而他被放到了宏工作的 eventqueue 外面,(2)往下查看看有没有微工作,发现 Promise 回调函数内的代码是同步的(微工作)输入 2
(3)then 函数把他放入了微工作序列。
(4)主代码块(宏工作)输入 4
(5)主线过程所有代码执行完结。先从微工作 queue 里拿回掉函数,输入 3 微工作全副实现
(6)再从宏工作的 queue 拿函数。输入 1

代码块 2

console.log('1');
setTimeout(function() {console.log('2');
    process.nextTick(function() {console.log('3');
    })
    new Promise(function(resolve) {console.log('4');
        resolve();}).then(function() {console.log('5')
    })
})
process.nextTick(function() {console.log('6');
})
new Promise(function(resolve) {console.log('7');
    resolve();}).then(function() {console.log('8')
})
setTimeout(function() {console.log('9');
    process.nextTick(function() {console.log('10');
    })
    new Promise(function(resolve) {console.log('11');
        resolve();}).then(function() {console.log('12')
    })
})

1. 主代码块输入 1
2. 宏工作 1setTimeout(2,3,4,5)
3. 微工作 1process(6)
4.promise 立刻执行,回调是同步输入 7,then 微工作 2(8)
5. 宏工作 2setTimeout(9,10,11,12)
主代码全副执行完(余微工作 1,2,宏工作 1,2)
6. 执行微工作 1,输入 6
7. 执行微工作 2,输入 8
微工作全副执行完(余宏工作 1,2)
8. 执行宏工作 1,输入 2,
增微工作 process(3)
promise 立刻执行回调输入 4,微工作(5),
9. 执行微工作输入 3,输入 5
10. 执行宏工作 2,输入 9,
增微工作 process(10)
promise 立刻执行回调输入 11,微工作(12),
11. 执行微工作输入 10,输入 12

代码块 3

async function async1() {console.log('async1 start');
    await async2();
    console.log('async1 end');
}
async function async2() {console.log('async2');
}
console.log('script start');
setTimeout(function() {console.log('setTimeout');
}, 0)
async1();
new Promise(function(resolve) {console.log('promise1');
    resolve();}).then(function() {console.log('promise2');
});
console.log('script end');

1. 输入“script start”
2.setTimeout 宏工作
3. 执行 async1() 输入 async1 start,执行 async2(),输入“async2”。
4.async2 执行结束,将 await async2 前面的代码退出到 微工作队列 async1 end’);
5、继续执行,new Promise, 同步输入“promise1”。promise.then,退出到微工作队列,
6. 输入 script end
7. 以后宏工作执行结束,查看微工作队列输入 async1 end“promise2”
8. 微工作全副执行完,查看宏工作,输入 setTimeout

改下面代码:

async function async1() {console.log('async1 start');
    let p = await async2();
    console.log(p);
    console.log('async1 end');
}
async function async2() {console.log('async2');
    return new Promise(((resolve, reject) => {resolve(10);
    }))
}
console.log('script start');
setTimeout(function() {console.log('setTimeout');
}, 0)
async1();
new Promise(function(resolve) {console.log('promise1');
    resolve();}).then(function() {console.log('promise2');
});
console.log('script end');

script start
async1 start
async2
promise1
script end
promise2
10
 async1 end
setTimeout
new promise 的操作就跟你 new 一个一般函数没区别,所以这一句其实是宏工作,但前面的 then 是微工作
resolved 后的 promise 对象会在这该级别事件队列完结之后才开始执行,及执行与该轮微工作队列中,始于下一级别宏工作之前
resolved 的 Promise 是在本轮事件循环的开端执行,总是晚于本轮循环的同步工作。

如果呈现两个这种微工作,则先呈现的会先执行

async 函数中,遇到 await 会跳出以后函数,并让出线程,再将 await 前面的代码放到 微工作(microtask)队列中
退出移动版