高级前端进阶,详解JavaScript中的事件循环机制

前端APP 投稿 52600 0 评论

高级前端进阶,详解JavaScript中的事件循环机制

详解JavaScript中的事件循环机制!!!

一、简单讲解

这个大家应该或多或少都知道的

for (var i = 0; i < 10; i++ {
  setTimeout(( => {
    console.log(i; // 输出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 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

我相信,你现在应该理解掌握了!

只要钻的深,铁杵都能给你磨成针!!!

编程笔记 » 高级前端进阶,详解JavaScript中的事件循环机制

赞同 (86) or 分享 (0)
游客 发表我的评论   换个身份
取消评论

表情
(0)个小伙伴在吐槽