javascript分为同步任务和异步任务。
打开网站时,网页的渲染过程就是同步任务,比如页面骨架和页面元素的渲染。
而像加载图片音乐之类占用资源大耗时久的任务,一般来说就是异步任务。
以下为javascript事件循环图
先执行同步任务,再执行异步任务
let data = [];
$.ajax({
url:www.javascript.com,
data:data,
success:() =>{
console.log('发送成功');
}
})
console.log('代码执行结束');
上面是一段简易的ajax代码:
(1) ajax进入Event Table,注册回调函数success
(2) 执行console.log('代码执行结束')
(3)ajax事件完成,回调函数success进入Event Queue。
(4)主线程从Event Queue读取回调函数success并执行。
SetTimeout:
// 延时3秒执行
setTimeout(() => {
console.log('延时3秒');
},3000)
setTimeout(()=>{
task();
},3000)
console.log('执行console')
先执行console,再执行task(),如果console部分代码用时很久,则task执行时间会超过3秒。
setTimeout(()=>{
task();
},3000)
sleep(1000000)
需要等sleep(100000)执行完毕后,task()才会执行,所以task()执行延迟不止3秒
我们还经常遇到setTimeout(fn,0)这样的代码,0秒后执行又是什么意思呢?是不是可以立即执行呢?
不会立即执行,setTimeout(fn,0)的含义是:
指定某个任务在主线程最早可得的空闲时间执行,意思就是不用再等多少秒了,只要主线程执行栈内的同步任务全部执行完成,栈为空就马上执行。
举例:
console.log('logAAA')
setTimeout(()=>{
console.log('setAAA')
},0);
// logAAA setAAA
setTimeout(()=>{
console.log('setBBB')
},0);
console.log('logBBB')
// logBBB setBBB
JS先执行同步代码,再执行异步代码,所以不论setTimeout写在前还是后,都是先执行console
SetInterval:
对于setInterval(fn,ms)来说,我们已经知道不是每过ms秒执行一次fn,而是每过ms秒就会有fn进入Event Queue。
一旦setInterval的回调函数fn执行时间超过了延时时间,那么就完全看不出来有时间间隔了。
除了广义的同步任务和异步任务外,我们对任务还有更精细的定义:
-
macro-task(宏任务):包括整体代码script,setTimeout,setInterval
-
micro-task(微任务):Promise.then,process.nextTick,object.observe
不同类型的任务会进入对应的Event Queue,
比如setTimeout和setInterval会进入相同的Event Queue(事件队列)。
事件循环的顺序,决定js代码的执行顺序。
进入一次整体代码执行宏任务后,开始第一次循环。
直接执行所有的微任务。
然后再次从宏任务开始,找到其中一个任务队列执行完毕。
再执行所有的微任务。
setTimeout(()=>{
console.log('setTimeout')
})
new Promise((resolve)=>{
console.log('promise')
}).then(()=>{
console.log('then');
})
console.log('console')
// 输出顺序为: promise --> console --> then --> setTimeout
(1) 这段代码做为宏任务,进入住线程
(2)先遇到setTimeout,将其回调函数注册后分发到宏任务Event Queue。
(3)接下来遇到Promise,newPromise立即执行,then函数分发到微任务Event Queue。
(4)遇到console,立即执行
(5)此时,整体代码做为第一个宏任务执行结束,看看有哪些微任务,
发现then在微任务Event Queue里面,执行。
(6)第一轮事件循环结束,开始第二次事件循环,从宏任务Event Queue开始,发现setTimeout。
(7)结束
//1 7 6 8 2 4 3 5 9 11 10 12