2011-07-26 36 views
40

這似乎很愚蠢,但我無法找到如何做一個與jQuery不涉及一些服務器端請求的異步函數調用。我有一個緩慢的函數,它遍歷了很多DOM元素,並且我希望瀏覽器在該函數運行時不會凍結。我想在慢速函數被調用之前顯示一個小指示器,然後當慢速函數返回時,我想隱藏指示器。我有以下幾點:jQuery異步函數調用,沒有AJAX請求

$('form#filter', parentNode).submit(function() { 
    var form = $(this); 
    indicator.show(); 
    var textField = $('input#query', form); 
    var query = jQuery.trim(textField.val()); 
    var re = new RegExp(query, "i"); 
    slowFunctionCall(); // want this to happen asynchronously; all client-side 
    indicator.hide(); 
    return false; 
}); 

目前,我提交表單並沒有顯示指示,瀏覽器凍結,然後slowFunctionCall完成。

編輯:我用Vivin's answer,特別是Sitepoint link得到以下解決方案:

var indicator = $('#tagFilter_loading', parentNode); 
indicator.hide(); 
var spans = $('div#filterResults span', parentNode); 
var textField = $('input#query', parentNode); 
var timer = undefined, processor = undefined; 
var i=0, limit=spans.length, busy=false; 
var filterTags = function() { 
    i = 0; 
    if (processor) { 
    clearInterval(processor); 
    } 
    indicator.show(); 
    processor = setInterval(function() { 
    if (!busy) { 
     busy = true; 
     var query = jQuery.trim(textField.val()).toLowerCase(); 
     var span = $(spans[i]); 
     if ('' == query) { 
     span.show(); 
     } else { 
     var tagName = span.attr('rel').toLowerCase(); 
     if (tagName.indexOf(query) == -1) { 
      span.hide(); 
     } 
     } 
     if (++i >= limit) { 
     clearInterval(processor); 
     indicator.hide(); 
     } 
     busy = false; 
    } 
    }, 1); 
}; 
textField.keyup(function() { 
    if (timer) { 
    clearTimeout(timer); 
    } 
    /* Only start filtering after the user has finished typing */ 
    timer = setTimeout(filterTags, 2000); 
}); 
textField.blur(filterTags); 

這顯示和隱藏的指示,也不會凍結瀏覽器。您可以在工作時看到隱藏的DOM元素,這正是我所要做的。

+0

我想所有的JS運行在一個單一的線程。所以不可能運行異步函數。但我可能錯了:) – PeeHaa

+1

根據你如何訪問正在減慢你的函數的DOM元素,你可能會加快這一部分。你使用類或屬性過濾器(緩慢)訪問與JQuery元素?函數是否運行多次,並且有什麼方法可以緩存與之交互的元素/ ID?你可以通過改變所有受影響的元素使用的CSS類來做同樣的DOM更新嗎? – tomfumb

回答

30

Javascript運行在一個單獨的線程,因此如果你有一個緩慢的功能,它阻止其他的一切。

UPDATE

那會做一些你想要的東西,但要記住,他們不是廣泛支持IE支持(我認爲他們將在IE10)。

Web的一些工人資源:

這裏是實現多線程沒有網絡工作者幾資源。需要注意的是,這是不是「真正的」多線程是很重要的:

+0

好吧,難怪我找不到這個教程...謝謝。 –

+0

您好,先生,過去應該是正確的,現在有些瀏覽器支持網絡工作者。 – nwellcome

+1

@nwellcome我也提到過它,但它不是一個可行的選擇,因爲它沒有得到廣泛的支持。我希望最終他們會得到廣泛的支持,因爲這會非常有用。 –

6

我正要去建議看看超時但唉。 John Resig(jQuery的)這篇文章解釋了一些關於JavaScript如何處理其單線程的內容。 http://ejohn.org/blog/how-javascript-timers-work/

This article還解釋說:「有一件事在JavaScript異步執行功能時,需要記住,在頁面中所有其他的JavaScript執行暫停,直到一個函數調用完成。這是當前所有的瀏覽器如何執行JavaScript,並能引起。如果你試圖同時異步地調用太多的東西,真正的性能問題。一個長時間運行的函數實際上會爲用戶「鎖定」瀏覽器。這同樣適用於同步的函數調用。」

所有這一切說,這是可能的,你可以模擬一個異步功能,打破你正在做的到更小的塊無論循環使用setTimeout調用自己()。

例如這樣的功能:

// Sync 
(function() { 
    for(var x = 0; x < 100000; ++x) {console.log(x)} 
})() 

// ASync 
var x = 0; 
setTimeout(function() { 
    console.log(x++); 
    if(x < 100000) setTimeout(arguments.callee, 1); 
} ,1) 
+0

如果你不想使用arguments.callee(自從ES5以來它在嚴格模式下被禁止),你可以給回調函數一個名字並在接下來的調用中使用它的名字。例如: var x = 0; setTimeout(function rec(){ console.log(x ++); if(x <100000)setTimeout(rec,1); },1) – danpop

5

你可能想web workers!

編輯:我很驚訝有多少人跳樓「它不可能」,所以我會詳細說明。網絡工作者是HTML 5 spec的一部分。它們允許你派生線程在後臺運行腳本而不會阻塞UI。它們必須是外部的js文件,由

var worker = new Worker('my_task.js'); 

調用並通過事件傳送。

+0

可能是某種東西。儘管如此,該頁面顯示「像往常一樣,後臺線程(包括工作人員)無法操作DOM」。 –

+0

Gooood點,工人不能訪問'window','document'或'parent',所以沒有DOM操作。 – nwellcome

+0

@nwellcome,這不是他們*不*可能:)我對Web Workers的可能性感到非常興奮。問題是他們沒有得到廣泛的支持(還)。 –