javascript是一门单线程语言,在最新的HTML5中提出了Web-Worker,但javascript是单线程这一核心仍未改变。因此一切javascript版的"多线程"都是用单线程模拟出来的,一切javascript多线程都是纸老虎!(无论是什么新框架新语法糖实现的所谓异步,其实都是用同步的方法去模拟的)
事件循环是js实现异步的一种方法,也是js的执行机制。 首先浏览器会把主任务队列中的同步任务挨个所有执行完,而后再去等待任务队列中看哪一个任务能够执行了, 而后把该执行的任务放到主任务队列中去执行,等这个任务执行完, 再去等待任务中看谁能够执行了,再把这个任务放到主任务队列中执行... 如此循环。 这种循环叫作事件循环(Event Loop)
js是单线程,js任务也要一个一个顺序执行。若是一个任务耗时过长,那么后一个任务也必须等着。那么问题来了,假如咱们想浏览新闻,可是新闻包含的超清图片加载很慢,难道咱们的网页要一直卡着直到图片彻底显示出来?所以聪明的程序员将任务分为两类:1)同步任务 2)异步任务javascript
一张图表示事件循环
![]()
#1.同步和异步任务分别进入不一样的执行"场所",同步的进入主线程,异步的进入Event Table并注册函数。 #2.当指定的事情完成时,Event Table会将这个函数移入Event Queue。 #3.主线程内的任务执行完毕为空,会去Event Queue读取对应的函数,进入主线程执行。 #4.上述过程会不断重复,也就是常说的Event Loop(事件循环)。 主线程执行栈什么时候为空? js引擎存在monitoring process进程,会持续不断的检查主线程执行栈是否为空,一旦为空,就会去Event Queue那里检查是否有等待被调用的函数。~~~~
因此能够看作是这样的:
1.浏览器线程先执行同步任务,途中遇到异步任务就将其加入到等待任务队列中去,而后继续向下执行,
2.等同步任务所有执行完毕后,再去等待任务队列中去将全部可执行的微任务逐个执行,
3.执行完微任务后在拿取第一个先到达执行条件的宏任务来执行,
4.执行完后再去等待任务队列中清理执行完全部已到达执行条件的微任务,
5.而后再拿取下一个宏任务来执行,若是宏任务执行产生微任务或者微任务执行产生宏任务就同样加入到等待任务队列中,而后仍是按照主线程每次到等待队列中先执行完因此的微任务再逐个执行宏任务的顺序来走java
异步任务都是谁先到达条件谁先执行,可是谁先到达执行条件也有优先级的问题,这个优先级要看这个任务是宏任务仍是微任务;微任务的优先级比宏任务的要高;程序员
在事件循环中,每进行一次循环操做称为 tick,每一次 tick 的任务处理模型是比较复杂的,但关键步骤以下:promise
* 执行一个宏任务(栈中没有就从事件队列中获取) * 执行过程当中若是遇到微任务,就将它添加到微任务的任务队列中 * 宏任务执行完毕后,当即执行当前微任务队列中的全部微任务(依次执行) * 当前宏任务执行完毕,开始检查渲染,而后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');
五、继续执行,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
若是出现两个这种微任务,则先出现的会先执行async