1. 程式人生 > >JS異步執行之setTimeout 0的妙用

JS異步執行之setTimeout 0的妙用

圖片 工作 focus body 事件 dom元素 音樂播放器 function utf-8

  最近在工作中遇到一些問題,大致是關於js執行問題的。由於沒搞清執行順序,導致出現了一些奇怪的bug。 所以這裏整理一些有關異步執行的知識(冰山一角角)...

  大家都知道js是單線程的,執行起來是順序的,在順序的業務邏輯中當然沒有問題。如果遇到可以並發執行的業務邏輯,再排隊就很低級了,所以需要異步執行!

1、什麽是異步?

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

console.log(1);
// 先打印 1 // 再打印 0

  比方說有些飯店你去吃飯需要提前預定(異步代碼執行),等其他人吃完(同步代碼執行完畢)你才能去,因此在其他人吃飯的時候(同步代碼執行中

)你可以去幹其他的事情,等其他人吃完了(同步代碼執行完畢)會有人來通知你,於是你可以去了(開始執行異步代碼【setTimeout/setInterval/事件處理程序/ajax回調...】的執行)。

  我們回到前面那段setTimeout身上,它的工作原理是這樣的,當你定義setTimeout那一刻起(不管時間是不是0),js並不會直接去執行這段代碼,而是把它扔到一個事件隊列裏面,當頁面中所有同步任務都幹完了以後,才會去執行事件隊列裏面的代碼。什麽是同步,按代碼順序執行,就像音樂播放器裏的順序播放。

2、setTimeout 0的妙用

<!DOCTYPE html>
<html>
  <head>
    <title> </title>
    <meta charset="utf-8">
  </head>
  <body>
  <p>
    <input type="text" id="input" value=""/>
    <span id="preview"></span>
  </p>
  </body>   
<script type="text/javascript">
(
function(){ function $(id){ return document.getElementById(id); } $(‘input‘).onkeypress = function(){ $(‘preview‘).innerHTML = this.value; } })(); </script> </html>

  這個keypress函數原意是監聽到用戶輸入字符串就將其完整的顯示出來,但是奇怪的是最後一個字符串總是沒能顯示出來:

  技術分享圖片

  重點來了,使用setTimeout 0來解決:

 $(‘input‘).onkeypress = function
(){ setTimeout(function(){ $(‘preview‘).innerHTML = $(‘input‘).value;},0); }

  好的,問題就這樣解決了,首先回過頭來看看問題是如何產生的?

  代碼中,我們用到的事件是keypress,而keypress事件發生時,dom元素的狀態還未改變,keypress事件之後dom元素的狀態才發生改變,通過setTimeout 0延遲執行就能達到期望的結果了。當然,不用setTImeout 0 ,直接用onkeyup亦可。

  但是setTimeout有些小小的問題,就是時間不精確:

<!DOCTYPE html>
<html>
  <head>
    <meta name="generator" content="">
    <title></title>
    <meta charset="utf-8">
  </head>
  <body>
    <h2>未使用 <code>setTimeout</code></h2>
    <button id="makeinput">生成 input</button>
    <p id="inpwrapper"></p>
  </body>   
<script type="text/javascript">
(function(){

  function get(id){
    return document.getElementById(id);
  }
  function log(a){
    console.log(a)
  }

  window.onload = function(){
    get(‘makeinput‘).onmousedown=function(){
      var input = document.createElement(‘input‘);
      input.setAttribute(‘type‘, ‘text‘);
      input.setAttribute(‘value‘, ‘test1‘);
      get(‘inpwrapper‘).appendChild(input);
      input.onfocus=function(){    //給生成的input綁定focus事件
         log("iptFocus");
      }
      input.focus();
      log("down");
    }
    get(‘makeinput‘).onfocus = function(){
      log("btnFocus");
    }
    get(‘makeinput‘).onclick = function(){
     log("click");
    }
    get(‘makeinput‘).onmouseup=function(){
      log("up");
    }
   }
})();
</script>
</html>
// 打印 iptFocus
// 打印 down
// 打印 btnFocus 導致新增的input無焦點的真兇
// 打印 up
// 打印 click

  將 input.focus(); 改為 setTimeout(function(){input.focus();},0);得到結果為:

// 打印 down
// 打印 btnFocus 
// 打印 iptFocus  鬼知道為什麽會跑到這執行
// 打印 up
// 打印 click

  

  理解了js的異步執行和setTImeout 0的工作原理,能更好的方便我們解決工作中的bug...

JS異步執行之setTimeout 0的妙用