JS 的单线程
单线程:同一时间只能执行一个任务,只有把上一件事情干完,才能干下一件事情(这会造成线程阻塞)
线程阻塞:前面的任务耗时过长,导致后面的代码不能执行
所以,对于事件、ajax 请求、定时器等非常耗时的程序,浏览器会开辟其他线程来处理,我们称这样的程序为异步程序
定时器是异步程序,所以 for
循环已经走完后,才会执行定时器内的语句
for (let i = 0; i < 5; i++) {
setTimeout(() => {
console.log(i); // 0 1 2 3 4
}, 1000);
}
let
定义的变量,具有块级作用域。定时器在该作用域内,只能访问变量 i
指定的值
同步任务 & 异步任务
- 同步任务:在 JS 主线程排队执行的任务,形成执行栈 Call Stack
- 异步任务:不进入主线程,进入浏览器开辟的新线程 Browser APIs 的任务,形成任务队列 Event Queue
异步任务只有任务队列通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行
setTimeout(() => {
console.log(1);
}, 0)
for (let i = 0; i < 10000; i++) {
console.log(2);
}
// for 循环执行完后,定时器才会执行
回调函数
- 函数作为参数传递给另一个函数,这个参数函数,就是回调函数
function b(fn) {
fn()
}
b(function () {
console.log(1)
})
- 异步任务一定有回调函数,但有回调函数不一定是异步任务
setTimeout(function () {
console.log(1);
}, 100);
JS 代码的执行顺序
- 所有同步任务都在主线程上,形成一个执行栈 Call Stack
- 主线程外,还存在一个任务队列 Event Queue。只要异步任务有了运行结果,就在任务队列中放置一个事件
- 执行栈中的同步任务执行完毕后,系统就会读取任务队列
- 主线程不断重复上面的第 3 步