事情原由是這樣的,由於頁面上有多個類似的異步請求動做,本着提升代碼可重用性的原則,我封裝了一個名爲getData的函數,它接收不一樣參數,只負責獲取數據,而後把數據return。基本的邏輯剝離出來是這樣的:php
代碼以下 | 複製代碼 |
function getData1(){ var result; $.ajax({ url : 'p.php', async : false, success: function(data){ result = data; } }); return result; } |
這裏的ajax不能用異步的,不然函數返回時,result還未賦值,會出錯。因此我加了async:false。看起來好像沒什麼問題。我調用這個函數能夠正常的獲得數據。html
代碼以下 | 複製代碼 |
$('.btn1').click(function(){ var data = getData1(); alert(data); }); |
接下來,要加另一個功能,因爲ajax請求有必定的耗時,因此我須要在發出請求前頁面有個loading效果,即顯示一張「正在加載」的gif圖片,想必你們也都見過。因此個人處理函數就變成了這樣:ajax
代碼以下 | 複製代碼 |
$('.btn1').click(function(){ $('.loadingicon').show(); var data = getData1(); $('.loadingicon').hide(); alert(data); }); |
請求以前顯示loading圖片,請求完成後把它隱藏。看起來也沒什麼問題。爲了看清效果,個人p.php代碼sleep了3秒,以下:chrome
<?phpsleep(3);echo ('aaaaaa');?>
可是我運行的時候問題出現了,我點擊按鈕並未像預想的那樣出現這個loading圖片,頁面什麼反應也沒有。排除良久找到了緣由,就在async:false這裏。promise
瀏覽器的渲染(UI)線程和js線程是互斥的,在執行js耗時操做時,頁面渲染會被阻塞掉。當咱們執行異步ajax的時候沒有問題,但當設置爲同步請求時,其餘的動做(ajax函數後面的代碼,還有渲染線程)都會中止下來。即便個人DOM操做語句是在發起請求的前一句,這個同步請求也會「迅速」將UI線程阻塞,不給它執行的時間。這就是代碼失效的緣由。瀏覽器
setTimeout解決阻塞問題
既然明白了問題在哪裏,咱們就來針對性想辦法。爲了避免讓同步ajax請求阻塞線程,我想到了setTimeout,把請求的代碼放到sestTimeout中,讓瀏覽器重啓一個線程來操做,不就解決問題了嗎?因而乎,個人代碼就變成了這樣:異步
代碼以下 | 複製代碼 |
$('.btn2').click(function(){ $('.loadingicon').show(); setTimeout(function(){ $.ajax({ url : 'p.php', async : false, success: function(data){ $('.loadingicon').hide(); alert(data); } }); }, 0); }); |
setTimeout的第二個參數設爲0,瀏覽器會在一個已設的最小時間後執行。無論三七二十一先運行起來看看。async
結果loading圖片顯示出來了,可是!!!圖片怎麼不動呢,我明明是一張動態gif圖。這個時候我很快就想到了,雖然同步請求延遲執行了,可是它執行期間仍是會把UI線程給阻塞。這個阻塞至關牛逼,連gif圖片都不動了,看起來像一張靜態圖片同樣。ide
結論很明顯,setTimeout治標不治本,至關於把同步請求「稍稍」異步了一下,接下來仍是會進入同步的噩夢,阻塞線程。方案失敗。函數
是時候用Deferred了
jQuery在1.5版本以後,引入了Deferred對象,提供的很方便的廣義異步機制。詳情可參看阮一峯老師的這篇文章 因而我用Deferred對象改寫了代碼,以下:
代碼以下 | 複製代碼 |
|
能夠看到我在ajax請求中去掉了async:false,也就是說,這個請求又是異步的了。另外請注意success函數中的這一句:defer.resolve(data),Deferred對象的resolve方法可傳入一個參數,任意類型。這個參數能夠在done方法中拿到,因此咱們異步請求來的數據就能夠以這樣的方式來返回了。
至此,問題獲得瞭解決。Deferred對象如此強大且方便,咱們能夠好好利用它。
個人所有測試代碼以下,有意的同窗能夠拿去測一下:
代碼以下 | 複製代碼 |
|
PS:Firefox有作優化?
上述問題在chrome和IE9中測試結論一致。可是我在Firefox中測試時,同步ajax並未阻塞掉UI線程,也就是說這個問題根本不存在。我用其餘代碼作了測試,在Firefox中js線程確實是會阻塞UI線程,這個沒有疑問。那可能的一個猜想就是Firefox對同步ajax作了優化,事實究竟是什麼,我暫未得知。有高人知道還請指點。
來自:http://www.cnblogs.com/haogj/p/4480772.html