js异步

js异步编程读书笔记(一)
  1. js提供的异步函数包括两类:
    1)I/O函数:js采用的是非阻塞式I/O,需要我们添加事件处理器,使用回调函数来实现阻塞。(node.js——事件驱动型服务器框架)
    2)计时函数:setTimeout、setInterval(不精确,触发频率较慢约200次/s);
    若想实现细粒度计时:

    • node:使用 process.nextTick,触发频率约10万次/s
    • requestAnimationFrame js动画函数——支持(ie9+)浏览器,60+帧/s的速度运行js动画,同时避免后台选项卡运行这些动画,节约CPU周期。
  2. js事件处理器在线程空闲之前不会运行,eg1:

    var start=new Date();
        setTimeout(function(){
             var end=new Date();
             console.log("time spend:",(end-start),"ms");
        },500);
        while(new Date-start<1000){};

    结果分析:

    • 运行的结果均随机>1000ms,因为setTimeout回调在while循环结束后才被触发。
    • 调用延时函数时,会将延时事件加入事件队列,回调执行的时间取决于队列中之前的事件的执行速度,可见setTimeout、setInterval计时精度不准。
    • 表示的延迟时间并不代表到时候一定会执行动画代码,而仅代表到时候会把代码添加到任务队列中。如果 UI 线程繁忙,比如忙于处理用户操作,那么即使把代码加入队列也不会立即执行。

    eg2:常见的for循环中使用计时函数,会出现叹为观止的结果:

    for (var i = 1; i < 6; i++) {
            setTimeout(function timer(){ //计时函数
                alert(i);//输出6,6,6,6,6
            },1000*i);//每秒执行一次
        console.log(i);//for循环线程控制台打印结果
        }

    按照思维定式:我们会预期计时函数执行结果与for循环控制台打印结果一致,均为1,2,3,4,5;然而结果却与设想大相径庭:计时函数的执行结果竟然为5个6;原因如上所示:

    • 即使在循环中,延迟函数的回调也要在循环结束时才执行
    • 循环过程中,延迟函数会加入到事件队列中排队等待,for循环执行完毕后,i=6,此时开始执行队列中的延时事件,setTimeout()调用其中的回调函数,回调函数形成了闭包,保持对外部作用域变量i的引用,因此队列中的延时函数执行时都调用的是i=6这个值,最终输出5个6.
    • 即使把定时器的时延改为0,setTimeout(…,0);依旧要异步等待,所有回调函数依旧在循环结束后才执行,且结果同上。
    • 此种方式还会带来其他头疼的问题,如果加入事件队列中的计时事件很多,当队列中的事件正在执行过程中,我们想停止它们的执行,刷新页面后,未完成的延时事件会继续执行,无法达到立即停止的效果,这样用户体验会很差。

    如果想得到我们预期的结果,应该怎样修改循环体呢?
    可以通过声明匿名函数并立即调用来创建一个新的作用域,在每次循环中把新的作用域封闭起来,把外部变量赋给封闭作用域内的一个新变量,这样就可以在每次循环中正常执行延时函数的回调函数,代码如下所示:

    //解决循环中延时函数无法立即执行的问题
        for (var i = 1; i < 6; i++) {
            (function(j){
            setTimeout(function timer(){
                alert(j);//输出1,2,3,4,5
            },1000*j); 
            })(i);
        //console.log(i);
        }
可以看出,得到了我们需要的结果。
如果把延时时间j*1000改成1000,又会出现神奇的结果,第一次循环输出1后,后面几次循环输出的数字不一定按顺序,可能会随机变化,比如1,3,2,5,4.如果需要严格按序执行,这样的结果可能会带来较严重的问题。
出现此情况的原因:每次循环延时函数的延时时间相同,导致事件加入队列等待执行时间出现冲突,因此结果会随机变化。为避免这种情况,依旧需要采用j*1000这种模式,保证每个延时事件有间隔(理想情况下每隔1s)的执行。

3. requestAnimationFrame(HTML5):
- 常用于js动画,方便浏览器确定重绘的最佳方式。浏览器都会对重绘操作加以限制,不超过显示器的重绘频率(60Hz),因此最平滑动画的最佳循环间隔是 1000ms/60,约17ms。
- 是一个全局函数。调用requestAnimationFrame后,它会要求浏览器根据自己的频率进行一次重绘,它接收一个回调函数作为参数,在即将开始的浏览器重绘时,会调用这个函数,并会给这个函数传入调用回调函数时的时间作为参数。由于requestAnimationFrame的功效只是一次性的,所以若想达到动画效果,则必须连续不断的调用requestAnimationFrame
- requestID = window.requestAnimationFrame(callback); // Firefox 23 / IE10 / Chrome / Safari 7 (incl. iOS)
requestID :可以作为参数传给window.cancelAnimationFrame() 来取消这个回调函数。
- callback:在每次需要重新绘制动画时,会调用这回调函数。这个回调函数会收到一个参数,这个 DOMHighResTimeStamp 类型的参数指示当前时间距离开始触发 requestAnimationFrame 的回调的时间。
- 如下是MDN开发者网络上面给出的兼容各浏览器的demo:

window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;

            var start = null;
            var d = document.getElementById('SomeElementYouWantToAnimate');
            function step(timestamp) {
              if (start === null) start = timestamp;
              var progress = timestamp - start;
              d.style.left = Math.min(progress/10, 200) + "px";
              if (progress < 2000) {   /
                requestAnimationFrame(step);//重复调用requestAnimationFrame实现动画循环效果
              }
            }
            requestAnimationFrame(step);//step表示实现动画操作的回调函数
  1. web worker(HTML5)

    • 是运行在后台的 JavaScript,不会影响页面的性能。(除了IE)在网页外单独开启一个线程执行任务,可缓存结果。
    • 一旦它被创建,Web Workers就可以通过postMessage()向任务池发送任务请求,执行完之后再通过postMessage()返回消息给创建者指定的事件处理程序(通过onmessage进行捕获)。
    • Web Workers进程能够在不影响用户界面的情况下处理任务,并且,它还可以使用XMLHttpRequest来处理I/O,无论responseXML和channel属性是否为null。
    • web worker 位于外部文件中,它们无法访问下例 JavaScript 对象:window 对象、 document 对象、parent 对象。
    • 深入理解参考:http://blog.csdn.net/i10630226/article/details/51536404 (多线程处理)
    • 下面的例子创建了一个简单的 web worker,在后台计数:

      <!DOCTYPE html>
      <html>
      <body>
      
      <p>Count numbers: <output id="result"></output></p>
      <button onclick="startWorker()">Start Worker</button>
      <button onclick="stopWorker()">Stop Worker</button>
      <br /><br />
      
      <script>
      var w;  //worker对象
      
      function startWorker()
      {
      if(typeof(Worker)!=="undefined")
      {
        if(typeof(w)=="undefined")
          {
          w=new Worker("demo_workers.js"); //检测是否存在worker,如不存在,创建一个新的worker对象,并执行"demo_workers.js" 
          }
        w.onmessage = function (event) {   //添加"onmessage"事件监听器
      "           document.getElementById("result").innerHTML=event.data; //当web worker传递消息时,会执行事件监听器中的代码
      "
        };
      }
      else
      {
      document.getElementById("result").innerHTML="Sorry, your browser
      does not support Web Workers...";
      }
      }
      
      function stopWorker()
      {
      w.terminate(); //终止web worker,并释放浏览器/计算机资源
      }
      </script>
      
      </body>
      </html>
      //外部"demo_workers.js"文件内容:
      var i=0;
      function timedCount()
      {
      i=i+1;
      
      postMessage(i);  //用于向 HTML 页面传回一段消息
      
      setTimeout("timedCount()",500);
      }
      timedCount();

    以上为部分读书笔记的总结,后续完善内容待补充……

    参考书籍及js异步编程相关链接

    《JavaScript异步编程:设计快速响应的网络应用》
    《you don’t know js》
    http://www.ruanyifeng.com/blog/2012/12/asynchronous%EF%BC%BFjavascript.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值