• 事件循环学习2


    主线程运行的时候,产生堆和栈(堆heap栈stack)
    栈中的代码调用各种外部API,他们在“任务队列”中加入各种事件(click,load,done)。只要栈中的代码执行完毕,主线程就会去读取“任务队列”,依次执行那些事件所对应的回调函数。
    执行栈中的代码(同步任务),总是在读取“任务队列”(异步任务)之前执行。
    eg:

    var req = new XMLHttpRequest();
    req.open(''GET',url);
    req.onload = function(){};
    req.onerror = function(){};
    req.send();

    上面代码中的req.send方法是Ajax操作向服务器发送数据,他是一个异步任务,意味着只有当前搅拌的所有代码执行完,系统才会去读取“任务队列”。所以,他与下面的写法等价。

    var req = new XMLHttpRequest();
    req.open();
    req.send();
    req.onload = function(){};
    req.onerror = function(){};

    也就是说,指定回调函数的部分 onload和onerror,在send()方法的前面或者后面无关紧要,因为他们属于执行栈的一部分,系统总是执行完他们,才会去读取“任务队列”。

    定时器
    出了放置异步任务的事件,“任务队列”还可以放置定时事件,即 指定某些代码在多少时间之后执行。这叫做“定时器”功能,也就是定时执行的代码。
    定时器功能主要由setTimeout()和setInterval()这两个函数来完成,他们的内部运行机制完全一样,区别在于前者指定的代码是一次性执行,后者则为反复执行。
    setTimeout()接受两个参数,一个是回调参数,第二个是推迟执行的毫秒数。

    console.log(1);
    setTimeout(function(console.log(2))}, 1000);
    console.log(3);

    上面代码执行的结果是1,3,2。因为setTimeout()将第二行推迟到1000毫秒之后执行。
    如果将setTimeout()第二个参数设为0,就表示当前代码执行完以后,也就是执行栈清空后,才会立即执行(0毫秒间隔)指定的回调函数。

    setTimeout(function(){console.log(1);}, 0);
    console.log(2);

    上面代码的执行结果是2,1。因为只有在执行完第二行以后,系统才会去执行“任务队列”中的回调函数,也就是setTimeout()。
    总之,setTimeout(fn,o)的含义是,指定某个任务在主线程最早可得的空闲时间执行,也就是说,尽可能早的执行。他在“任务队列”的尾部添加一个事件,因此要等到同步任务和“任务队列”现有的事件都处理完,才会得到执行。
    HTML5标准规定了setTimeout()的第二个参数的最小值(最短间隔),不得低于4毫秒,如果低于这个值,就会自动增加。在此之前,老版本的浏览器都将最短间隔设为10毫秒。另外,对于那些DOM的变动(尤其是涉及页面重新渲染的部分),通常不会立即执行,而是每16毫秒执行一次。这时使用requestAnimationFrame()的效果汉语 setTimeout().

    需要注意的是,setTimeout()只是将事件插入了“任务队列”,必须等到当前代码执行完,(执行栈清空)。主线程才会去执行他指定的回调函数。要是当前代码耗时很长,有可能要等很久,所以并没有办法保证,回调函数一定会在setTimeout()指定的时间执行。

    Node.js的Event Loop
    Node.js也是单线程的EventLoop,但是他的运行机制不同于浏览器环境。

    Node.js的运行机制如下

    1.V8引擎解析JavaScript脚本。
    2.解析后的代码,调用NodeAPI。
    3.libuv库复杂Node API的执行。他将不同的任务分配给不同的线程,形成一个Event Loop(事件循环),以异步的方式将任务的执行结果返回给V8引擎。
    4.V8引擎再将结果返回给用户。

    除了setTimeout和setInterval这两个方法,Node.js还提供了另外两个与“任务队列”有关的方法:
    process.nextTick和setImmediate。
    他们可以帮助我们加深对“任务队列”的理解。

    process.nextTick方法可以在当前“执行栈”的尾部---下一次EventLoop(主线程读取“任务队列”)之前,触发回调函数。也就是说,他指定的任务总是发生在所有异步任务之前。
    setImmediate方法则是在当前“任务队列”的尾部添加事件,也就是说,他指定的任务总是在下一次EventLoop时执行,这与setTimeout(fn,o)很像。
    e.g

    process.nextTick(function A(){
      console.log(1);
      process.nextTick(function B(){
        console.log(2);
      })
    });
    
    setTimeout(function timeout(){
      console.log('timeout');
    }, 0)

    执行结果:1,2,timeout

    上面代码中,由于process.nextTick方法指定的回调函数,总是在当前“执行栈”的尾部触发,所以不仅函数A比setTimeout指定的回调函数timeout先执行,而且函数B也比timeout先执行。
    这说明,如果有多个process.nextTick语句,不管他们是否嵌套,将全部在当前“执行栈”执行。

    再看setImmediate

    setImmediate(function A(){
      console.log(1);
      setImmediate(function B(){
        console.log(2);
      });
    });
    setTimeout(function timeout(){
      console.log('timeout');
    }, 0)

    上面代码中,setImmediate与setTimeout(fn,o)各自添加了一个回调函数A和timeout,都是在下一次EvebtLoop触发。那么 运行的结果是不确定的。

    可能是1,timeout,2
    也有可能是timeout,1,2
    但是在Node.js文档中说,setImmediate指定的回调函数,总是排在setTimeout前面。

  • 相关阅读:
    tf_upgrade_v2.exe实验
    tf.random_uniform出错tensorflow2.0出错
    Tensorflow2.0变化
    Anaconda安装PyTorch
    Anaconda是如何进行版本管理的?
    CUDA开发指南
    Tensorflow视频教程&Pytorch视频教程
    Applied Spatiotemporal Data Mining应用时空数据挖掘
    GAN one-shot
    基于深度学习的图像超分辨率重建 一种基于非负矩阵分解的高光谱影像模拟方法
  • 原文地址:https://www.cnblogs.com/benyu-aimao/p/8931376.html
Copyright © 2020-2023  润新知