JS異步執行之setTimeout 0的妙用
最近在工作中遇到一些問題,大致是關於js執行問題的。由於沒搞清執行順序,導致出現了一些奇怪的bug。 所以這裏整理一些有關異步執行的知識(冰山一角角)...
大家都知道js是單線程的,執行起來是順序的,在順序的業務邏輯中當然沒有問題。如果遇到可以並發執行的業務邏輯,再排隊就很低級了,所以需要異步執行!
1、什麽是異步?
setTimeout(function(){ console.log(0); },0) console.log(1);
// 先打印 1 // 再打印 0
比方說有些飯店你去吃飯需要提前預定(異步代碼執行),等其他人吃完(同步代碼執行完畢)你才能去,因此在其他人吃飯的時候(同步代碼執行中
我們回到前面那段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的妙用