同步和异步
学习预知(前言/引言):
在编程世界中,同步和异步是两种截然不同的代码执行范式,它们决定了程序如何响应时间消耗操作(如网络请求、文件读写等)。同步代码像一条单行道,必须等待前车通过才能继续前进;而异步代码则像立交桥,通过回调、Promise或事件驱动让程序在等待时依然能处理其他任务。
学习本文后,你将掌握:
- 同步的代价:理解为何
for循环会冻结界面,而setTimeout不会 - 异步的魔力:从回调地狱到
async/await的进化之路 - 底层机制:事件循环如何像调度中心一样管理任务队列
- 实战抉择:何时用同步保顺序,何时用异步提性能
关键认知突破:
- 异步不是"同时执行",而是"不必等待"
- 单线程的JavaScript如何通过事件循环实现高并发
Promise和setTimeout在事件循环中的不同命运
准备好颠覆你对程序执行的认知了吗?让我们从一段被setTimeout欺骗的代码开始……
1. 进程:
在不同的场景中都可以用来描述改场景中的一个效果,线程是进程里面的一个更小的单位,通常多个线程配合工作构成一个进程。
-
V8 运行一份 js 代码,会创建一份进程,从上往下执行代码,遇到同步代码直接执行,遇到异步代码就跳过,先去执行后面的同步代码全部执行完毕后再回过头来执行异步代码。
-
js默认是单线程语言:因为 js 的设定是为了做浏览器的脚本语言,尽量少的开销用户的设备的性能
有手段弄成多线程,但是情况很少一般不考虑。
2. 线程
实例
let a = 1 //同步代码
setTimeout(() =>{ //异步代码
a = 2
console.log(a,'ZHT');
},1000)
console.log(a);
结果:
a = 1
a = 2 ZHT
- 其中() =>{ a = 2 console.log(a,'ZHT'); }为回调函数,等里面的进行完了再回过来调用
定时器 setTimeout() 和 setInterval() 的区别,setTimeout() 延迟指定时间后执行一次,单次延迟操作 setInterval() 每隔指定时间重复执行,周期性重复操作
同步:我们把不耗时的代码称为同步代码。
异步: 我们把耗时的代码称为异步代码。
对于 V8 来说只有定时器才会耗时,其余的都不耗时。但是对于计算机的角度的话会根据计算机的性能都耗时。
处理异步
- 因为 js 的这种执行规则,导致我们在开放过程中的时而会出现代码异步的情况。
- 回调函数
function xq() {
setTimeout(() => {
console.log('我们去相亲');
marry()
}, 1000)
}
function marry() {
console.log('我们结婚了');
}
xq()
当嵌套过深时,代码的可读性差,维护困难,排查问题困难(回调地狱)
function xq() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('我们去相亲');
resolve() //最后一步执行
}, 2000)
})
}
function marry() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('我们结婚啦!');
resolve()
}, 1000)
})
}
function baby() {
setTimeout(() => {
console.log('我们生小宝宝啦!');
}, 500)
}
xq()
.then(() => {
return marry()
})
.then(() => {
baby()
})
then的源代码里面也返回了一个 promise 实例对象,状态默认继承自 xq 函数返回的对象状态
xq() 里面执行到了reslove()后面的 .then才会触发
V8 引擎的原理:
执行 xq 函数,立即返回一个 promise 实例对象,但是此时该对象的状态是 pending(等待状态)
.then 立即触发,但是 then 里面的回调函数没有触发
等待 xq 函数里面的 reslove() 执行完毕,此时实例对象的状态会变更为fulfillde(成功状态),此时 .then 里面的回调函数会触发执行