概述
我们都知道js是一个单线程,里面的的请求方式分为两种,一种是同步请求,一种是异步请求,同步方法先执行完毕,然后再去异步任务队列中查看有没有异步任务,有才会执行。
在JavaScript中,所有的任务都可以分为
同步任务:立即执行的任务,同步任务一般会直接进入到主线程中执行
异步任务:异步执行的任务,比如ajax
网络请求,setTimeout
定时函数等
常见的宏任务
script
、setTimeout
、setInterval
、setImmediate
常见的微任务
一个需要异步执行的函数,执行时机是在主函数执行结束之后、当前宏任务结束之前
Promise
中的then、async、await、process.nextTick
(Node.js)、MutationObserver
(监听DOM变化的事件)、Object.observe
(已废弃;Proxy 对象替代)
关于两者之间的区别:微任务是批量执行、宏任务则是一个一个的执行。
- 执行一个宏任务,如果遇到微任务就将它放到微任务的事件队列中
- 当前宏任务执行完成后,会查看微任务的事件队列,然后将里面的所有微任务依次执行完
这里我们可以看到,script标签包含的下面的代码块,那么这就属于是第一个异步任务,也就是宏任务,首先去执行它
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| <script> console.log('1') setTimeout(function () { console.log('2') }); new Promise(function (resolve) { console.log('3'); resolve(); }).then(function () { console.log('4') setTimeout(function () { console.log('5') }); }); new Promise(function (resolve) { console.log('6'); resolve(); }).then(function () { console.log('7') setTimeout(function () { console.log('8') }); }); </script>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| setTimeout(() => { console.log(0) }, 0) new Promise((resolve, reject) => { console.log(1) resolve() }).then(() => { console.log(2) new Promise((resolve, reject) => { console.log(3) resolve() }).then(() => { console.log(4) }).then(() => { console.log(5) }) }).then(() => { console.log(6) }) new Promise((resolve, reject) => { console.log(7) resolve() }).then(() => { console.log(8) })
172384650
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
| console.log(1); new Promise((resolve, reject) => { console.log(2); setTimeout(() = >{ console.log(3) }); resolve() }).then(() =>{ console.log(4) });
setTimeout(() =>{ console.log(5) }); console.log(6)
1,2,6,4,3,5
<script> console.log('1') setTimeout(function () { console.log('2') }); new Promise(function (resolve) { console.log('3'); resolve(); }).then(function () { console.log('4') setTimeout(function () { console.log('5') }); }); new Promise(function (resolve) { console.log('6'); resolve(); }).then(function () { console.log('7') setTimeout(function () { console.log('8') }); }); console.log(9) </script>
136947258
let promise = new Promise(resolve=>{ setTimeout(()=>{ resolve(); console.log('1') },0); console.log('2') }).then(value=>console.log('3')) console.log('4') 2413
console.log(1) // 遇到 console.log(1) ,直接打印 1
// 遇到定时器,属于新的宏任务,留着后面执行 setTimeout(()=>{ console.log(2) }, 0)
// 遇到 new Promise,这个是直接执行的,打印 'new Promise' new Promise((resolve, reject)=>{ console.log('new Promise') resolve() }).then(()=>{ // .then 属于微任务,放入微任务队列,后面再执行 console.log('then') })
console.log(3) // 遇到 console.log(3) 直接打印 3
// 好了本轮宏任务执行完毕,现在去微任务列表查看是否有微任务,发现 .then 的回调,执行它,打印 'then' // 当一次宏任务执行完,再去执行新的宏任务,这里就剩一个定时器的宏任务了,执行它,打印 2
实际结果是:1=>'new Promise'=> 3 => 'then' => 2
|
总结
同步>微任务>宏任务
async与await
async
是异步的意思,await
则可以理解为 async wait
。所以可以理解async
就是用来声明一个异步方法,而 await
是用来等待异步方法执行
async
函数返回一个promise
对象,下面两种方法是等效的
1 2 3 4 5 6 7 8
| function f() { return Promise.resolve('TEST'); }
async function asyncF() { return 'TEST'; }
|
正常情况下,await
命令后面是一个 Promise
对象,返回该对象的结果。如果不是 Promise
对象,就直接返回对应的值。不管await
后面跟着的是什么,await
都会阻塞后面的代码
1 2 3 4 5 6 7 8 9 10 11 12
| async function fn1 (){ console.log(1) await fn2() console.log(2) }
async function fn2 (){ console.log('fn2') }
fn1() console.log(3)
|
上面的例子中,await
会阻塞下面的代码(即加入微任务队列),先执行 async
外面的同步代码,同步代码执行完,再回到 async
函数中,再执行之前阻塞的代码
所以上述输出结果为:1,fn2,3,2
参考文章