详解JavaScript中的事件循环机制!!!
这个大家应该或多或少都知道的
for (var i = 0; i < 10; i++) { setTimeout(() => { console.log(i); // 输出10个10 });}解析:先执行for循环,循环叠加i,然后再执行setTimeout 10遍,所以会输出10个10
变体:
for (let i = 0; i < 10; i++) { setTimeout(() => { console.log(i); // 依次输出0-9 });}我们知道var跟let本质区别就是作用域的问题,所以会联想到let导致的,但在这一点上,并不是作用域问题,因为从事件循环机制来解释是有问题的
以上的代码等价于
for (var i = 0; i < 10; i++) { (function (i) { setTimeout(() => { console.log(i); }); })(i); // 立即执行函数,以上的let应该是将代码特殊处理了下}立即执行函数:函数声明后立即执行。
从事件循环的角度来说,应该先执行for循环将i叠加到10,执行完后再去执行for循环体(setTimeout),此时的i变成了10,所以每次输出都是10,
这里使用let,应该是特殊处理了一下的。
我们知道JavaScript语言是单线程的,至于为啥是单线程?
假设有两个线程,一个在页面上新增一个div,另一个线程在页面上删除div,那最终听谁的?
那JavaScript怎么实现异步的呢?
在JavaScript中,有两类任务:同步任务和异步任务。
同步任务:普通的任务,依次从上往下执行。
异步任务:又分为宏任务、微任务。
宏任务:setTimeout跟setInterval
微任务:Promise().then() 这里要注意一下,Promise方法里面的是同步任务,then里面的才是微任务
执行顺序:先执行同步任务,遇到异步任务,将其放入到宏任务或者微任务队列中,然后优先执行微任务,接下来再去执行宏任务。
setTimeout(function() { console.log(1)}, 0);new Promise(function(resolve, reject) { console.log(2); // 这里是同步任务 resolve();}).then((res) => { console.log(3);})console.log(4);// 输出结果是 2 4 3 1解析:从上往下执行,setTimeout是宏任务,放到宏任务队列中,
Promise里面的是同步任务,所以先执行,输出2,then里面的是微任务,放到微任务队列中,
最后一个是同步任务,执行,输出4,
然后执行微任务,输出3
最后执行宏任务,输出1
console.log(1);setTimeout(function () { console.log(2);}, 0);setTimeout(function () { console.log(3); setTimeout(function () { console.log(4); }, 0);}, 0);new Promise(function (resolve, reject) { console.log(5); resolve();}).then((res) => { console.log(6); new Promise(function (resolve, reject) { console.log(7); resolve(); }).then((res) => { console.log(8); });});new Promise(function (resolve, reject) { console.log(9); resolve();}) .then((res) => { console.log(10); }) .then((res) => { console.log(11); });console.log(12);// 输出 1 5 9 12// 6 7 10 8 11// 2 3 4解析:原理跟上面一样,不过需要注意的是,8跟11的顺序:,在这里then的层级,将各个Promise里面第一层的then放在一起,第二层的then放在一起,依此类推,
然后依次执行第一层的,执行完第一层,接下来执行完第二层,依此类推。
第一层(6,7,10)
第二层(8,11)
所以先输出8, 再输出11的。
console.log(1);async function fn() { console.log(2); await console.log(3); console.log(4); // 这一步,你应该会有问题}setTimeout(() => { console.log(5);}, 0);fn();new Promise((resolve) => { console.log(6); resolve();}).then(() => { console.log(7);});console.log(8);// 输出:1 2 3 6 8 4 7 5解析:async await是Promise的语法糖,只是针对写法上的优化
将async await翻译成Promise:
async function fn() { console.log(2); await console.log(3); console.log(4); // 这一步,你应该会有问题}// 等价于function fn() { return new Promise((resolve, reject) => { console.log(2); resolve( (() => { console.log(3); })() // 立即执行函数 ); }).then(() => { console.log(4); });}这样,你应该能够明白,为啥输出3之后不是立即输出4了吧!
console.log(1);async function fn() { console.log(2); await console.log(3); await console.log(4); console.log(5);}setTimeout(() => { console.log(6);}, 2000);setTimeout(() => { console.log(7);}, 1000);fn();new Promise((resolve) => { console.log(8); new Promise((resolve) => { console.log(9); resolve(); }).then(() => { console.log(10); }); resolve();}).then(() => { console.log(11); new Promise((resolve) => { console.log(12); resolve(); }).then(() => { console.log(13); });});console.log(14);// 输出 1 2 3 8 9 14// 4 10 11 12 5 13// 7 6 
只要钻的深,铁杵都能给你磨成针!!!