JavaScript 是单线程语言。单线程就意味着需要排队,前一个任务完成才能执行下一个任务。所以任务分为两种,同步任务和异步任务。
- 同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行下一个任务。
- 异步任务指的是不进入主线程,而进入“任务队列”的任务,只有任务队列通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
不同的任务源会被分配到不同的 Task 队列中,任务源可以分为 微任务(microtask)和 宏任务(macrotask)。
- 宏任务:包括整体代码script,setTimeout,setInterval
- 微任务:Promise.then(非new Promise),process.nextTick(node中)
异步执行的运行机制如下:(同步执行可以视为没有异步任务的异步执行)
- 所有同步任务都在主线程上执行,这属于宏任务,形成一个执行栈。先执行宏任务在执行微任务
- 主线程之外,还存在一个任务队列。异步任务只要有了运行结果,就在任务队列中放置一个事件。
- 执行栈中的所有同步任务执行完毕,系统就会读取任务队列,将对应的异步任务放入执行栈,开始执行微任务
- 主线程不断重复上面的第三步。
主线程丛任务队列中读取事件,这个过程是循环不断的,所以这种运行机制又称为 Event Loop(事件循环)
除了放置异步任务的事件,任务队列还可以放置定时事件。定时器功能主要由setTimeout()和setInterval()这两个函数来完成,它们的内部运行机制完全一样,区别在于前者指定的代码是一次性执行,后者则为反复执行。
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')
})
})
输出:
1
2
4
3
5
1. 宏任务同步代码console.log('1'),不多说
2. setTimeout,加入宏任务Event Queue,没有发现微任务,第一轮事件循环走完
3. 第二轮事件循环开始,先执行宏任务,从宏任务Event Queue中独取出setTimeout的回调函数
4. 同步代码console.log('2'),发现process.nextTick,加入微任务Event Queue
5. new Promise,同步执行console.log('4'),发现then,加入微任务Event Queue
6. 宏任务执行完毕,接下来执行微任务,先执行process.nextTick,然后执行Promise.then
7. 微任务执行完毕,第二轮事件循环走完,没有发现宏任务,事件循环结束