由于JS任务是单线程执行的,即同一时间只能完成一项任务,后面的任务只有等前面的任务完成才能进行,如果前面的任务很耗时会导致后面的任务无法进行,影响总体的进程
为了解决该问题,防止主线程阻塞,因此出现了同步任务和异步任务
同步任务
在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务,形成一个执行栈。为非耗时任务
异步任务
耗时任务进入任务队列,当主线程中的任务执行完毕,就从任务队列中取出任务放进主线程中来进行执行。
由于主线程不断重复的获取任务、执行任务、再获取再执行,所以这种机制被叫做事件循环。
异步任务又分为宏任务和微任务
宏任务(macrotask)
是由宿主(浏览器发起的)
微任务(microtask)
是由JS自身发起的
JS任务执行顺序
先执行主线程任务,当主线程存在微任务时先执行微任务,微任务执行完后开始执行宏任务,一个宏任务执行完后,检查是否有微任务,有微任务则先执行微任务执行完毕后再执行宏任务。如果一个宏任务中包含宏任务,则将所包含的宏任务加入任务队列等待执行。
测试实例
实例一
宏任务中包含宏任务
setTimeout(() => {
console.log(' 1--start');
setTimeout(() => {
console.log('2--macrotask')
}, 0)
new Promise(resolve => {
resolve()
console.log('3--new promise')
}).then(() => {
console.log('4--promise then')
})
}, 0)
setTimeout(() => {
console.log('5--macrotask')
}, 0)
console.log('6--end')
//执行顺序613452
实例二
async await
注意:Promise是异步的,但是new Promise在实例化的过程中所执行的代码都是同步的,.then中的回调是异步的
async await的底层是基于Promise进行的封装,因此async相当于new Promise的实例化过程,是同步的,而await相当于new Promise中的.then回调,是异步的
async function async1() {
console.log('1--async1 start');
await async2();
console.log('2--async1 end');
}
async function async2() {
console.log('3--async2');
}
console.log('4--script start');
setTimeout(function() {
console.log('5--setTimeout');
}, 0)
async1();
new Promise(function(resolve) {
console.log('6--promise1');
resolve();
}).then(function() {
console.log('7--promise2');
});
console.log('8--script end');
//执行顺序 41368275
注意:Promise是异步的,但是new Promise在实例化的过程中所执行的代码都是同步的,.then中的回调是异步的
async await的底层是基于Promise进行的封装,因此async相当于new Promise的实例化过程,是同步的,而await相当于new Promise中的.then回调,是异步的
实例三
const bar = () => console.log('1--bar')
const baz = () => console.log('2--baz')
const foo = () => {
console.log('3--foo')
setTimeout(bar, 0)
new Promise((resolve, reject) =>
resolve('4--应该在 baz 之后、bar 之前')
).then(resolve => console.log(resolve))
baz()
}
//执行顺序 3241
实例四
多个script标签
<script>
console.log(1);
setTimeout(() => {
console.log(5);
});
new Promise((resolve) => {
resolve();
}).then(() => {
console.log(3);
});
console.log(2);
</script>
<script>
console.log(4);
</script>
//执行顺序 12345
script标签本身是作为宏任务来存在的