目录
一、定义
1、JavaScript单线程
2、同步任务(synchronous)
3、异步任务(asynchronous)
二、任务队列(task queue)
三、代码举例说明
四、扩展一下setTimeout
1 、同步代码执行完了 setTimeout会从0计时吗
2、两个定时器 上面的定时器先执行 在执行下面的定时器吗?
3、定义一个变量为0 设置两个一样的定时器事件 他会输出什么结果 ?
五、面试题练习
1、第一个 promise+setTimeout
2、第二个promise+setTimeout
3、aync/awit
4、promise的时候
5、微任务队列中创建微任务
6、微任务队列中创建宏任务
JavaScript是单线程指的是同一时间只能干一件事情,只有前面的事情执行完,才能执行后面的事情。导致遇到耗时的任务时后面的代码无法执行。
console.log('1-0');
console.log('2-0');
for (let i = 1; i <= 5; i++) {
console.log(i);
}
得到的一定是顺序执行:
setTimeout(() => {
console.log('定时器');
}, 0)
console.log('长歌行');
按普通的执行顺序来说 定时器在上面 应该先输出定时器 在输出 长歌行
但实际结果: 长歌行 定时器
原因是 setTimeout是异步任务
1.宏任务
页面中的大部分任务都是在主线程上执行的,这些任务包括了:
为了协调这些任务有条不紊地在主线程上执行,页面进程引入了消息队列和事件循环机制,渲染进程内部会维护多个消息队列,比如延迟执行队列和普通的消息队列。然后主线程采用一个 for 循环,不断地从这些任务队列中取出任务并执行任务。我们把这些消息队列中的任务称为宏任务。
2.微任务
基于微任务的技术有 MutationObserver、Promise 以及以 Promise 为基础开发出来的很多其他的技术。所以微任务的重要性也与日俱增,了解其底层的工作原理对于你读懂别人的代码,以及写出更高效、更具现代的代码有着决定性的作用。
通过上面代码知道setTimeout是异步的 我们就搞清了执行顺序优先级 同步代码>异步代码 所以说 在任务队列中 分为两大类 1.同步任务 2. 异步任务
1.执行栈
(1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
(2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
(3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
(4)主线程不断重复上面的第三步,称为事件循环(Event Loop)。
宏任务中的方法:1. script (可以理解为外层同步代码,作为入口 ) 2. setTimeout/setInterval
微任务中的方法:1.Promise 2. nextTick
而他们的执行顺序 是 微任务 先输出 在输出 宏任务
setTimeout(() => {
console.log('定时器');
}, 0)
new Promise((resolve) => {
console.log('同步代码')
resolve('异步代码')
}).then((res) => {
console.log(res);
})
console.log('长歌行');
运行结果:
注意:new Promise是创建一个构造函数 这个过程是同步的,而.then方法是异步的 所以代码先执行 同步>微任务>宏任务
setTimeout(() => {
console.log('setTimeout');
}, 1000);
console.log('长歌行');
for (let i = 0; i < 1000; i++) {
console.log('');
}
运行结果:
setTimeout(() => {
console.log('setTimeout1');
}, 2000);
setTimeout(() => {
console.log('setTimeout2');
}, 1000);
运行结果:
i = 0
setTimeout(() => {
console.log(++i);
}, 1000);
setTimeout(() => {
console.log(++i);
}, 1000);
运行结果: 1 2
console.log('a1');
Promise.resolve().then(() => {
console.log('Promise-1');
setTimeout(() => {
console.log('setTimeout-2');
}, 0);
});
setTimeout(() => {
console.log('setTimeout-1');
Promise.resolve().then(() => {
console.log('Promise-2');
});
}, 0);
console.log('a2');
运行结果是:
a1
a2
Promise-1
setTimeout-1
Promise-2
setTimeout-2
运行解释:
第一轮
1.0 a1 第一轮 执行的全局任务。
1.1 Promise.resolve() 加入到第一轮的微任务队列中。
1.2 setTimeout('setTimeout-1') 生成宏任务队列一。
1.3 a2 第一轮 最后一个执行的全局任务,并检查第一轮微任务队列。
1.4 'Promise-1' 微任务被执行。
1.5 setTimeout('setTimeout-2') 生成宏任务队列二。
// 第一轮事件循环结束, 依次输出 a1, a2, Promise-1
// 第二轮
2.0 'setTimeout-1' 执行在第一轮生成宏任务队列1, 宏任务队列清空,检查微任务队列。
2.1 'Promise-2' 执行第二轮加入的微任务。
// 第二轮结束,依次输出 setTimeout-1,Promise-2
// 第三轮
3.0 'setTimeout-2' 执行第一轮生成的宏任务队列二,输出'setTimeout-2' 结束。
console.log('1');
setTimeout(function () {
console.log('2');
process.nextTick(function () {
console.log('3');
})
new Promise(function (resolve) {
console.log('4');
resolve();
}).then(function () {
console.log('5')
})
})
process.nextTick(function () {
console.log('6');
})
new Promise(function (resolve) {
console.log('7');
resolve();
}).then(function () {
console.log('8')
})
setTimeout(function () {
console.log('9');
process.nextTick(function () {
console.log('10');
})
new Promise(function (resolve) {
console.log('11');
resolve();
}).then(function () {
console.log('12')
})
})
运行结果:
1、7 、6、8、2、4、3、5、9、11、10、12
运行解释:
第一轮 执行外面同步代码 : 1 7
第二轮 执行 微任务 : 6 8
第三轮 宏任务 第一个setTimeout : 同步 2 4 微任务 3 5 第二个setTimeout:同步 9 11 微任务 10 12
console.log('script start')
async function async1() {
await async2()
console.log('async1 end')
}
async function async2() {
console.log('async2 end')
}
async1()
setTimeout(function() {
console.log('setTimeout')
}, 0)
new Promise(resolve => {
console.log('Promise')
resolve()
})
.then(function() {
console.log('promise1')
})
.then(function() {
console.log('promise2')
})
console.log('script end')
运行结果:
新版本---运行详解:
1、执行console.log('script start')
2、执行async1()
1、执行async2()
console.log('async2 end')
2、await async2()后边的内容放入微任务队列,让出主线程
3、setTimeout放入宏任务队列
4、执行Promise
1、console.log('Promise')
2、第一个then放入微任务队列
5、console.log('script end') 主线程执行完毕
6、执行微任务队列
1、执行console.log('async1 end')
2、执行第一个then console.log('promise1'),放入第二个then
3、执行第二个then console.log('promise2')
7、执行宏任务队列 console.log('setTimeout')
console.log('script start');
new Promise((resolve) => {
console.log('promise 1');
resolve();
}).then(() => {
console.log('promise 2');
})
console.log('script end');
运行结果:
script start
promise 1
script end
promise 2
promise声明了就会直接调用的哦。但是执行到promise的then方法就会将其放入微任务队列先不执行,继续执行后边的哦
运行详解:
1、promise在主线程上,执行promise,输出promise1
1、遇到setTimeout放入宏任务队列中
2、遇到promise1的then方法会放入微任务队列
2、执行微任务队列,里边有个promise
promise2的then方法放入微任务队列(也就是promise1的后边)
3、执行宏任务队列
console.log('script start');
new Promise((resolve) => {
console.log('Promise');
resolve();
}).then(() => {
console.log('Promise.then');
setTimeout(() => {
console.log('setTimeout1');
}, 1000)
})
setTimeout(() => {
console.log('setTimeout2');
}, 100)
console.log('script end');
运行结果:
图解:
1、promise在主线程上,执行promise,输出promise1
遇到promise的then方法会放入微任务队列
2、执行微任务队列,里边有个setTimeout
setTimeout放入微任务队列的宏任务队列中(也就是新建一个宏任务队列)
3、执行宏任务队列,100在前,1000在后,先输出setTimeout2,后输出setTimeout1
执行完文任务队列,会从宏任务队列中选出优先级较高的执行