单线程的概念
JS是一门单线程语言。因此,JS在同一个时间只能做一件事。单线程意味着,如果在同个时间有多个任务的话,这些任务就需要进行排队,前一个任务执行完,才会执行下一个任务。
为什么是单线程?
JS的单线程,与它的用途是有很大关系。我们都知道,JS作为浏览器的脚本语言,主要用来实现与用户的交互,利用JS,我们可以实现对DOM的各种各样的操作,如果JS是多线程的话,一个线程在一个DOM节点中增加内容,另一个线程要删除这个DOM节点,那么这个DOM节点究竟是要增加内容还是删除呢?这会带来很复杂的同步问题,因此,JS是单线程的。
任务队列
因为JS的单线程,所以同个时间只能处理同个任务,所有任务都需要排队,前一个任务执行完,才能继续执行下一个任务。但是,如果前一个任务的执行时间很长,比如文件的读取操作或ajax操作,后一个任务就不得不等着。拿ajax来说,当用户像后台获取大量的数据时,不得不等到所有数据都获取完毕才能进行下一步操作,用户只能在那里干等着,严重影响用户体验。
因此,JS在设计的时候,就已经考虑到这个问题,主线程可以完全不用等待文件的读取完毕或ajax的加载完成,可以先挂起处于等待中的任务,先运行排在后面的任务,等到文件的读取或ajax有了结果后,再回过头执行挂起的任务。因此,任务就可以分为同步任务和异步任务。
任务队列其实是一个先进先出的数据结构
JS的所有任务分类:同步任务和异步任务
主线程和任务队列的示意图
同步任务和异步任务
异步任务
异步任务是指不进入主线程,而进入任务队列的任务,只有任务队列通知主线程,某个异步任务可以执行了,该任务才会进入主线程。
举例Demo-1:
setTimeout(()=>{
console.log('setTimeout')
},0)
new Promise(resolve=>{
resolve() //无论resolve()放在上面还是下面,console.log('promise')都是同步优先执行
console.log('promise')
})
.then(value=>console.log('成功'))
console.log('执行顺序')
打印结果依次为:promise、执行顺序、成功、setTimeout
但是一定是向上面所说的执行吗?看下面的Demo-2
let promise=new Promise(resolve=>{
setTimeout(()=>{
//setTimeout这个宏任务不执行,那么里面的resolve()这个微任务就没办法创建
// 虽然优先级是:同步任务>微任务>宏任务
// 但是这个例子里面,宏任务优先于微任务执行
resolve()
console.log('setTimeout')
},0)
console.log('promise')
}).then(value=>console.log('成功'))
console.log('哈哈哈')
虽然优先级是:同步任务>微任务>宏任务,但是并不一定就是宏任务最后执行,具体情况具体分析
总的来说,JS的异步机制包含以下几个步骤:
1. 所有同步任务都在主线程上执行,形成一个执行栈
2. 主线程之外,还存在一个任务队列,只要异步任务有了结果,就会在任务队列中放置一个事件
3. 一旦执行栈中的所有同步任务执行完毕,系统就会读取任务队列,看看里面还有哪些事件,那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行
4. 主线程不断地重复上面的第3步
以下举例:看执行顺序:
async function async1(){
console.log('async1 start')
await async2()
console.log('async1 end')
}
async function async2(){
console.log('async2')
}
console.log('script start')
setTimeout(function(){
console.log('setTimeout')
},0)
async1();
new Promise(function(resolve){
console.log('promise1')
resolve();
}).then(function(){
console.log('promise2')
})
console.log('script end')
1. 从上到下,先走同步任务队列,再走异步任务队列
2. 同步任务:script start → async1 start → async2 → promise1 → script end
3. 微观任务:async1 end → promise2
4. 宏观任务:setTimeout
对Promise的理解
1. Promise是一个立即执行的同步函数
2. 当执行到.then .catch .finally .all .race等时,是异步的
3. Promise的首字母大写,Promise是一个构造函数,可以new Promise()得到一个Promise的实例
4. 每个Promise都有返回结果,返回结果就是Promise
new Promise(function(resolve) {
console.log('promise1');
resolve();
})
//同步执行
.then(function() {
console.log('promise2');
});
//异步
async await
1. await这一行以及它上面的都是同步执行(绿框同步立即执行)
2. await之后是异步的微任务(红线画出来的,放在微任务队列中稍后执行)