JavaScript是一门单线程的语言,意味着同一时间内只能做一件事情,但是并不意味着单线程就是阻塞,而实现单线程非阻塞的方法就是事件循环
所有的任务都分为同步任务、异步任务。
同步任务:立即执行的任务,同步任务一般会直接进入到主线程中执行
异步任务:ajax网络请求、setTimeout定时器函数
同步任务和异步任务流程图如下:
从上图中可以看到:同步任务进⼊主线程,即主执⾏栈,异步任务进⼊任务队列,主线程内的任务执 ⾏完毕为空,会去任务队列读取对应的任务,推⼊主线程执⾏。那么上述过程的不断重复就称为事件循环。
那再来说一下微任务和宏任务
微任务:
⼀个需要异步执⾏的函数,执⾏时机是在主函数执⾏结束之后、当前宏任务结束之前
常⻅的微任务有:
1.Promise.then
2.MutaionObserver
3.Object.observe(已废弃;Proxy 对象替代)
4.process.nextTick(Node.js)
宏任务:
宏任务的时间粒度⽐较⼤,执⾏的时间间隔是不能精确控制的,对⼀些⾼实时性的需求就不太符合
常⻅的宏任务有:
1.script (可以理解为外层同步代码)
2.setTimeout/setInterval
3.UI rendering/UI事件
4.postMessage、MessageChannel
5.setImmediate、I/O(Node.js)
按上图流程它的执行机制是:
执行一个宏任务,如果遇到微任务就将他放到微任务的事件队列中,当前宏任务执行完之后,会查看微任务的事件队列,然后将里面的所有微任务依次执行完毕
那么下面来说一下async以及await
async 就是⽤来声明⼀ 个异步⽅法,⽽ await 是⽤来等待异步⽅法执⾏
async用法:
function f() {
return Promise.resolve('TEST');
}
// asyncF is equivalent to f!
async function asyncF() {
return 'TEST';
}
await用法: await 命令后⾯是⼀个 Promise 对象,返回该对象的结果。如果不是 Promise 对 象,就直接返回对应的值
async function f(){
// 等同于
// return 123
return await 123
}
f().then(v => console.log(v)) // 123
不管 await 后⾯跟着的是什么, await 都会阻塞后⾯的代码
上⾯的例⼦中, await 会同步代码执⾏完,再回到 async 函数中,再执⾏之前阻塞的代码阻塞下⾯的代码(即加⼊微任务队列),先执⾏ async 外⾯的同步代码,
所以上述输出结果为: 1 , fn2 , 3 , 2