1.浏览器是单线程的,但可以利用事件流模拟多线程。代码有同步和异步(callback setTimeout setInterval Promise ...)的分别。
2. 内存里分为堆和栈,堆是存储像对象这类复杂数据类型的,而栈是存储基本数据类型和函数执行时的运行空间。
同步代码在执行栈中,异步代码放在队列中,只有执行栈中的代码执行完了,异步代码才会执行。
eg:
console.log(1);
setTimeout(() => {
console.log(2);
}, 0);
console.log(3);
输出结果 1, 3, 2;
同时异步代码分为宏任务(task)和微任务(microtask)
执行顺序:执行栈中的代码 => 微任务 => 宏任务
eg:
console.log(1)
setTimeout(() => {
console.log(2)
}, 0)
Promise.resolve().then(() => {
console.log(3)
})
输出结果:1,3,2
执行过程:
执行栈
执行栈
中的代码永远最先执行
微任务(microtask): promise MutationObserver...
- 当
执行栈
中的代码执行完毕,会在执行宏任务队列
之前先看看微任务队列
中有没有任务,如果有会先将微任务队列
中的任务清空才会去执行宏任务队列
宏任务(task): setTimeout setInterval setImmediate(IE专用) messageChannel...
- 等待
执行栈
和微任务队列
都执行完毕才会执行,并且在执行完每一个宏任务
之后,会去看看微任务队列
有没有新添加的任务,如果有,会先将微任务
队列中的任务清空,才会继续执行下一个宏任务
案例分析:
setTimeout(() => {
console.log('timeout1')
Promise.resolve().then(() => {
console.log('promise1')
})
Promise.resolve().then(() => {
console.log('promise2')
})
}, 100)
setTimeout(() => {
console.log('timeout2')
Promise.resolve().then(() => {
console.log('promise3')
})
}, 200)
过程分析:
- 先将两个
setTimeout
塞到宏任务队列中 - 当第一个
setTimeout1
时间到了执行的时候,首先打印timeout1,然后在微任务队列中塞入promise1
和promise2
- 当第一个
setTimeout1
执行完毕后,会去微任务队列检查是不是空的,他发现了有两个promise
,会把两个promise
按顺序执行完再去执行下一个宏任务 - 两个
promise
执行完毕后会微任务队列中没有任务了,会去宏任务中执行下一个任务setTimeout2
- 当
setTimeout2
执行的时候,先打印一个timeout2,然后又在微任务队列中塞了一个promise2
- 当
setTimeout2
执行完毕后会去微任务队列检查,发现有一个promise2,会将promise2
执行 - 会依次打印
timeout1 promise1 promise2 timeout2 promise3
浏览器和node中的事件流有些不一样