javascript事件环(EventLoop)

1. 基础知识

1. js语言特点

1. js语言是单线程语言,主线程是单线程。

2. js语言采用事件循环(EventLoop)机制。

2. 同步任务/异步任务

同步任务: 不被引擎挂起,在主线程等待执行的, 按照顺序执行的任务。

异步任务: 一般比较消耗时间,被引擎挂起,进入任务队列,等待主线程全部执行完才能执行的任务。

3. 事件循环机制

同步任务在主线程执行,异步任务进入任务队列,当主线程任务全部执行完成后,js引擎去查看异步任务队列,

如果满足条件,第一个异步任务进入主线程,变成同步任务。

再次全部执行完同步任务,然后继续查看任务队列,如此往复循环,直到队列为空,程序结束。

2. 异步操作(不等于异步任务)

异步操作只是手动的实现改变函数的调用顺序。不会进入任务队列。

常见的异步操作是回调函数/事件监听模式/发布订阅模式;

异步操作又可以分为串行异步/并行异步/串并行结合异步;

1. 回调函数

普通回调函数只是改变了函数的执行顺序,并不会异步任务,也不会进入任务队列。

function a(callback){
    callback(); // 回调函数
}
function b(){}
a(b); // b是a的回调函数

2. 事件监听

事件监听函数也是指定任务的执行顺序,不会进入任务队列。

  <button>click</button>
  <script>
    function a() {
      let button = document.querySelector('button');
      button.click(); //函数a触发监听事件
    }
    function b() {// 在a执行触发后,再执行
      console.log("triggered by a");
// 最后需要取消监听 } document.addEventListener(
'click', b); </script>

3.发布订阅

任务执行后向“消息中心”发布(publish)消息, 其他任务订阅(subscribe)消息。

// 伪代码
function a() {
   XXX.publish('done'); //  发布消息;XXX表示消息中心
}
function b() {}
//  a发布后引发b执行
XXX.subscribe('done', b);//  b订阅消息

// 如果b执行完,需要取消订阅!!!
XXX.unSubscribe('done', f2);

4. 串行异步--多个异步操作

有两种实现方式:

1)回调函数--逐级嵌套

下次执行等待上次执行结束,彼此之间有耦合。setTimeout是异步任务,会进入任务队列。

示例: 执行6次回调后执行final函数

    <script>
      function asyncFn(arg, callback) {
        console.log("参数:", arg);
        setTimeout(function(){
          callback(arg*2);
        }, 1000);
      }
      function final(value) {
        console.log("the final value equals ", value)
      }
      asyncFn(1, function(result) {
        asyncFn(result, function(result1) {
          asyncFn(result1, function(result2) {
            asyncFn(result2, function(result3){
              asyncFn(result3, function(result4){
                asyncFn(result4, function(result5){
                  final(result5);
                })
              })
            })
          })
        })
      })
      console.log('00')
    </script>
callback

执行结果如下:

参数:1 
00 
参数:2
参数:4
参数:8
参数:16
参数:32
the final value equals  64

2)自定义一个函数(递归函数)

<script>
      let items = [...new Array(6)].fill(1);
      function asyncFn(arg, callback) {
        console.log("参数:", arg);
        setTimeout(function(){
          callback(arg*2);
        }, 1000);
      }
      function final(value) {
        console.log("the final value equals ", value)
      }
      // 串行执行异步操作
      var paramValue = 1; // 初始化参数
      function serious(items) {
        if (items.shift()) {
          asyncFn(paramValue, function(result) {
            paramValue = result;
            serious(items);
          })          
        } else {
          final(paramValue)
        }
      }
      serious(items);
      console.log('00')
    </script>
serious

执行结果和上面一样。

两种方式的执行结果都是6秒。

5. 并行执行--多个异步

并行执行的异步函数,彼此之间不存在执行先后顺序问题,没有耦合。

几个异步函数同时执行,等待6个异步操作都执行完成后,执行final。

    <script>
      let items = [...new Array(6)].fill(1);
      function asyncFn(arg, callback) {
        console.log("参数:", arg);
        setTimeout(function(){
          callback(arg*2);
        }, 1000);
      }
      function final(value) {
        console.log("the final value equals ", value)
      }
      var result=[];
      items.forEach((item, index) => [
        asyncFn(index+1, function(res) {
          result.push(res);
          if (result.length === 6) { // 判断6个异步都执行完成
            final(res);
          }
        })
      ])
    </script>
View Code

执行结果如下:

参数:1
参数:2
参数:3
参数:4
参数:5
参数:6
the final value equals  12

并行执行完成耗时1秒

6. 串并行结合

串行异步操作耗时较多;并行效率,但是占用资源多,如果并行操作过多,很容易耗尽系统资源。

可以通过限制并行的个数来提高效率的同时,避免过分占用资源。

示例: 保证每次同时执行的异步任务是3个,等6个异步操作执行完成后执行final

<script>
      function asyncFn(arg, callback) {
        console.log("参数:", arg);
        setTimeout(function(){
          callback(arg*2);
        }, 1000);
      }
      function final(value) {
        console.log("the final value equals ", value)
      }
      let items = Array.from(new Array(6), (_,index)=> index+1);
      let result=[];
      let limit = 3; //最大3个
      let running = 0; // 正在执行的个数
      function both() {
        while(running < limit && items.length > 0){
          let item = items.shift();
          asyncFn(item, function(result) {
            running--;
            if (items.length > 0) {
              both();
            } else if (running ===0 ) { // 执行完毕
              final(result)
            }
          })
          running++;
        }        
      }
      both();
    </script>
both

执行结果同上。 

耗时2秒

猜你喜欢

转载自www.cnblogs.com/lyraLee/p/11734159.html
今日推荐