一分钟让你悟到什么是同步/异步、微任务/宏任务、Promise、async/await
JavaScript是一门单线程的语言
JavaScript是一门单线程
的语言,因此,JavaScript在同一个时间只能做一件事,单线程意味着,如果在同个时间有多个任务的话,这些任务就需要进行排队,前一个任务执行完,才会执行下一个任务
。(是的,你就当作在做排队核酸)
为什么是单线程
其实,JavaScript的单线程,与它的用途是有很大关系,我们都知道,JavaScript作为浏览器的脚本语言,主要用来实现与用户的交互,利用JavaScript,我们可以实现对DOM的各种各样的操作,如果JavaScript是多线程的话,一个线程在一个DOM节点中增加内容,另一个线程要删除这个DOM节点,那么这个DOM节点究竟是要增加内容还是删除呢?这会带来很复杂的同步问题,因此,JavaScript是单线程的
同步任务和异步任务是什么
所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。
同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;
异步任务指的是,不进入主线程、而进入”任务队列”(task queue)的任务,只有等主线程任务执行完毕,”任务队列”开始通知主线程,请求执行任务,该任务才会进入主线程执行
用官方一点的话来讲,当一个js被执行的时候,把同步代码全放到一个执行栈里面,异步代码放到一个任务队列中,当栈执行完毕后开始执行任务队列
function add(){
console.log(2)
}
function add3(){
console.log(5)
}
function add2(){
console.log(4)
}
console.log(1)
setTimeout(add,5000);
console.log(3)
setInterval(add2,1000)
setTimeout(add3,1000)
首先,同步的代码是console.log(1)和log(3),所以先输出了1,3,然后就是异步的地方了。
同步任务
同步任务是指在主线程上排队执行的任务,只有前一个任务执行完毕,才能继续执行下一个任务
,当我们打开网站时,网站的渲染过程,比如元素的渲染,其实就是一个同步任务。通俗来讲:就是上一个捅完核酸下一个才继续
异步任务
异步任务是指不进入主线程,而进入任务队列的任务,只有任务队列通知主线程,某个异步任务可以执行了,该任务才会进入主线程,
当我们打开网站时,像图片的加载,音乐的加载,其实就是一个异步任务。通俗来讲:就是上一个捅完核酸,到下一个的时候发现没网健康码半天转不出来了,那只能让后面有健康码的人先让大白捅了(自己到一边等网来吧)
function fun1() {
console.log(1);
}
function fun2() {
console.log(2);
}
function fun3() {
console.log(3);
}
fun1();
setTimeout(function(){
fun2();
},0);
fun3();
// 输出
1
3
2
有了异步,就算fun2()里面是文件的读取或ajax这种需要耗时的任务,也不怕fun3()要等到fun2()执行完才能执行啦
异步机制:JavaScript中的异步是怎么实现的呢?
那么,JavaScript中的异步是怎么实现的呢?那要需要说下回调和事件循环
这两个概念啦
首先要先说下任务队列,我们在前面也介绍了,异步任务是不会进入主线程,而是会先进入任务队列
,任务队列其实是一个先进先出
的数据结构,也是一个事件队列,比如说文件读取操作,因为这是一个异步任务,因此该任务会被添加到任务队列中,等到IO完成后,就会在任务队列中添加一个事件
,表示异步任务完成啦,可以进入执行栈啦~但是这时候呀,主线程不一定有空,当主线程处理完其它任务有空时,就会读取任务队列,读取里面有哪些事件,排在前面的事件会被优先进行处理,如果该任务指定了回调函数
,那么主线程在处理该事件时,就会执行回调函数中的代码,也就是执行异步任务啦
当执行异步任务的时候,异步任务中可能也包含同样的同步任务与异步任务,顺序还是一样的,就算任务队列中没有任务也会去执行,这个过程会无限循环下去,因此也叫作事件循环
异步编程:怎么才能实现异步编程,写出性能更好的代码呢
那么,怎么才能实现异步编程,写出性能更好的代码呢,下面有几种方式
1、回调函数
回调函数我们在使用ajax时应该用的很多啦,其实在使用ajax时,我们就用到了异步
var req = new XMLHttpRequest();
req.open("GET",url);
req.send(null);
req.onreadystatechange=function(){}
2、Promise
Promise 存在的意义是异步问题同步化解决方案
我们都知道Promise()
执行是同步的,而then()
执行是异步的,为什么?
为什么Promise()
执行是同步的,而 then()
执行是异步的呢?
让我们用代码来看看吧:
// 异步程序
const data = $.ajax({
url: "http://localhost:3000/data.json",
async: false // 同步
});
console.log(data.responseJSON.map(item=>item.name));
console.log("My name is LPieces.");
当我设置了async: false
上面的ajax和下面的打印就形成了同步的关系。虽然实现了我想要的结果,但是打印的My name is LPieces
跑到下面去了,它要等着上面ajax执行完才打印出来,那这就不对了哦,这会阻塞了下面所有的代码。既然这样,来看看Promise怎么帮我解决这个问题:
const p = new Promise((resolve, reject) => {
$.ajax({
url: 'http://localhost:3000/data.json',
success (data) {
resolve(data);
}
})
})
p.then(res => {
console.log(res.map(item=>item.name));
})
console.log("My name is LPieces.");
运行结果:
是不是完美的解决了阻塞的问题呀,到这里应该能理解为什么Promise()
的执行一定是同步的,而 then()
的执行一定是异步的了吧,如果then()
是同步执行的话,那么就会回到上面阻塞的问题去了,那我设置async: false
不香吗?干嘛还要写个Promise
包裹ajax的代码呢?
使用 Promise 完成一个异步
function wait(time) {
return new Promise(function(resolve,reject) {
setTimeout(resolve,time);
});
}
这个时候我们就可以使用Promise处理异步任务啦
wait(1000).then(function(){
console.log(1);
})
异步任务又分为宏任务和微任务
宏任务:普通任务,正常执行。正常的异步任务都是宏任务
,最常见的就是主代码块、定时器(setInterval, setImmediate, setTimeout)、IO任务
微任务:优先于宏任务执行
(但不会抢断)。微任务出现比较晚,queueMicrotask、Promise和async属于微任务(当然,async就是promise)
我们可以看出来,微任务的优先级比宏任务高,一个任务结束后,事件循环会找到并执行全部微任务,然后再查找其他任务,那么,怎么分别宏任务和微任务呢?
来看个例子
console.log('aaa');
setTimeout(() => console.log(111), 0); //异步任务
queueMicrotask(() => console.log(222)); //异步任务
console.log('bbb');
输出的结果是:
aaa
bbb
222
111
执行顺序就像我们之前说的,先执行同步的,也就是aaa,bbb,然后在执行异步的,但是这里,setTimeout
在queueMicrotask前面,却还是先执行了queueMicrotask,就是因为queueMicrotask是微任务,而setTimeout是宏任务,也就是说在异步中,会先完成所有的微任务,然后再去执行宏任务
为什么会有宏任务与微任务
既然是异步任务,我们当然要选择谁执行第一步谁执行第二步了。按照官方的设想就是,任务之间的不平等。有些任务对于用户而言更加重要,需要先执行,有些任务(类似定时器)晚点执行也没有什么问题
宏任务和微任务相当于一种优先级队列的方式
Promise 的奥特人间体:async/await
- 这两个玩意就是要基于Promise实现的,它不能用于普通的回调函数。它是解决回调地狱最好的方法【Promise + async awiat的结合】
- sync/await使得异步代码看起来像同步代码,这正是它的魔力所在。
一个函数如果加上 async
,那么该函数就会返回一个Promise
async function async1() {
return "1"
}
console.log(async1()) // -> Promise {<resolved>: "1"}
总结
说了这么多其实就是因为js是单线程就有了同步异步,而异步进入任务队列又分为了宏任务和微任务,而微任务里面出了个Promise
这玩意来搞同步(你可以当成有5个人健康码转不出来了,这时来了一个热心志愿者组织他们排好队,谁先转好谁再先来😂)
参考资料:
https://juejin.cn/post/7018011655665614884
https://juejin.cn/post/6962312899960242213
https://blog.csdn.net/qq_51649346/article/details/123953823
https://blog.csdn.net/weixin_43837268/article/details/91386754