发表于: 2020-04-16 23:26:40

1 2042


今天完成的事情:
明天计划的事情:
遇到的问题:
收获:今天写了一个短信倒计时的案例以及学习了js的执行队列

主要是使用和清除定时器的应用

<!DOCTYPE html>
<html>

<head>
</head>

<body οnlοad="init()">
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <input type="button" style="height:32px;width:120px;" value="点击发送验证码" onclick="sendCode(this)" />
  <script type="text/javascript">
    var clock = '';
    var nums = 10;
    var btn;
    function sendCode(thisBtn) {
      btn = thisBtn;
      btn.disabled = true//将按钮置为不可点击
      btn.value = nums + '秒后可重新获取';
      clock = setInterval(doLoop1000); //一秒执行一次
    }
    function doLoop() {
      nums--;
      if (nums > 0) {
        btn.value = nums + '秒后可重新获取';
      } else {
        clearInterval(clock); //清除js定时器
        btn.disabled = false;
        btn.value = '点击发送验证码';
        nums = 10//重置时间
      }
    }
  </script>

</body>

</html>

运行结果

点击

倒计时完后

还学习了一下代码的执行机制

 <script type="text/javascript">
    /*
    以下这段代码的执行结果是什么?
    如果依照:js是按照语句出现的顺序执行这个理念,
    那么代码执行的结果应该是:
        //"定时器开始啦"
        //"马上执行for循环啦"
        //"执行then函数啦"
        //"代码执行结束"
    但结果并不是这样的,得到的结果是:
        //"马上执行for循环啦"
        //"代码执行结束"
        //"执行then函数啦"
        //"定时器开始啦"
*/
    setTimeout(function () {
      console.log('定时器开始啦')
    });

    new Promise(function (resolve) {
      console.log('马上执行for循环啦');
      for (var i = 0i < 10000i++) {
        i == 99 && resolve();
      }
    }).then(function () {
      console.log('执行then函数啦')
    });

    console.log('代码执行结束');
  </script>

javascript版的"多线程"都是用单线程模拟出来的。

单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。

  如果排队是因为计算量大,CPU忙不过来,倒也算了,但是很多时候CPU是闲着的,因为IO设备(输入输出设备)很慢(比如Ajax操作从网络读取数据),不得不等着结果出来,再往下执行。

  JavaScript语言的设计者意识到,这时主线程完全可以不管IO设备,挂起处于等待中的任务,先运行排在后面的任务。等到IO设备返回了结果,再回过头,把挂起的任务继续执行下去。

  于是,所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行

1、同步和异步任务分别进入不同的执行"场所",同步的进入主线程,异步的进入Event Table并注册函数。

  2、当Event Table中指定的事情完成时,会将这个函数移入Event Queue。

  3、主线程内的任务执行完毕为空,会去Event Queue读取对应的函数,进入主线程执行。

  4、上述过程会不断重复,也就是常说的Event Loop(事件循环)。

  5、我们不禁要问了,那怎么知道主线程执行栈为空啊?js引擎存在monitoring process进程,会持续不断的检查主线程执行栈是否为空,一旦为空,就会去Event Queue那里检查是否有等待被调用的函数。

 其对任务还有更精细的定义:

    macro-task(宏任务):包括整体代码script,setTimeout,setInterval

    micro-task(微任务):Promise,process.nextTick

  不同类型的任务会进入对应的Event Queue。

  事件循环的顺序,决定js代码的执行顺序。进入整体代码(宏任务)后,开始第一次循环。接着执行所有的微任务。然后再次从宏任务开始,找到其中一个任务队列执行完毕,再执行所有的微任务。

 

"任务队列"是一个事件的队列(也可以理解成消息的队列),IO设备完成一项任务,就在"任务队列"中添加一个事件,表示相关的异步任务可以进入"执行栈"了。主线程读取"任务队列",就是读取里面有哪些事件。

  "任务队列"中的事件,除了IO设备的事件以外,还包括一些用户产生的事件(比如鼠标点击、页面滚动等等)。只要指定过回调函数,这些事件发生时就会进入"任务队列",等待主线程读取。

  所谓"回调函数"(callback),就是那些会被主线程挂起来的代码。异步任务必须指定回调函数,当主线程开始执行异步任务,就是执行对应的回调函数。

  "任务队列"是一个先进先出的数据结构,排在前面的事件,优先被主线程读取。主线程的读取过程基本上是自动的,只要执行栈一清空,"任务队列"上第一位的事件就自动进入主线程。但是,由于存在后文提到的"定时器"功能,主线程首先要检查一下执行时间,某些事件只有到了规定的时间,才能返回主线程。

  读取到一个异步任务,首先是将异步任务放进事件表格(Event table)中,当放进事件表格中的异步任务完成某种事情或者说达成某些条件(如setTimeout事件到了,鼠标点击了,数据文件获取到了)之后,才将这些异步任务推入事件队列(Event Queue)中,这时候的异步任务才是执行栈中空闲的时候才能读取到的异步任务。

剩下的明天再进行


返回列表 返回列表
评论

    分享到