2010-04-14 46 views
17

好吧,所以我明白,Javascript不是C#或PHP,但我一直回到Javascript的問題 - 不是與JS本身,但我的使用它。你怎麼讓JavaScript代碼執行*爲了*

我有一個函數:

function updateStatuses(){ 

showLoader() //show the 'loader.gif' in the UI 

updateStatus('cron1'); //performs an ajax request to get the status of something 
updateStatus('cron2'); 
updateStatus('cron3'); 
updateStatus('cronEmail'); 
updateStatus('cronHourly'); 
updateStatus('cronDaily'); 

hideLoader(); //hide the 'loader.gif' in the UI 

} 

事情是,由於JavaScript的強烈願望,在代碼中向前跳,從來沒有出現的裝載程序,因爲「hideLoader」功能後直接運行。

我該如何解決這個問題?或者換句話說,我怎樣才能使JavaScript函數按照我在頁面上寫的順序執行...

+0

Promise是處理所有異步請求的最佳對象。 [使用Promise對象修復異步請求](https://stackoverflow.com/a/47719849/7487135) – 2017-12-08 18:03:01

回答

14

問題發生是因爲AJAX的本質是異步。這意味着updateStatus()調用確實按順序執行,但是立即返回並且JS解釋器在從AJAX請求中重新獲取任何數據之前達到hideLoader()

對於AJAX調用完成的事件,您應該執行hideLoader()

+0

準確地說,JS不會等待updateStatus作業在開始下一個作業之前完成。如果cron1必須在cron2啓動之前完成,您需要將每個updateStatus設置爲在先前調用的成功時運行。 – MindStalker 2010-04-14 13:37:14

2

這與代碼的執行順序無關。

加載程序映像從不顯示的原因是在您的函數運行時UI不會更新。如果您在用戶界面中進行了更改,那麼在您退出該功能並將控制權返回給瀏覽器之前,它們不會出現。

您可以設置圖像後,使用超時,給瀏覽器有機會啓動代碼的其餘部分之前更新UI:

function updateStatuses(){ 

    showLoader() //show the 'loader.gif' in the UI 

    // start a timeout that will start the rest of the code after the UI updates 
    window.setTimeout(function(){ 
    updateStatus('cron1'); //performs an ajax request to get the status of something 
    updateStatus('cron2'); 
    updateStatus('cron3'); 
    updateStatus('cronEmail'); 
    updateStatus('cronHourly'); 
    updateStatus('cronDaily'); 

    hideLoader(); //hide the 'loader.gif' in the UI 
    },0); 
} 

有也可以讓你的代碼的另一個因素出現執行沒有秩序。如果你的AJAX請求是異步的,該函數將不會等待響應。當瀏覽器接收到響應時,處理響應的功能將運行。如果您希望在收到響應後隱藏加載器映像,那麼在最後一個響應處理函數運行時您必須這樣做。由於回覆不必按照您發送請求的順序到達,因此您需要計算在最後一次回覆時您需要知道多少次回覆。

+0

感謝Guffa和Sbaitso博士,我明白更多的人知道發生了什麼,我會嘗試一些建議,看看我能做些什麼! – 2010-04-14 13:41:55

1

安裝Firebug的,那麼這樣添加一行到每個showLoader,updateStatus和hideLoader的:

Console.log("event logged"); 

你會在控制檯窗口中看到列出你的函數調用,他們將爲了。問題是,你的「updateStatus」方法是做什麼的?

假設它啓動了一個後臺任務,然後返回,所以在任何後臺任務完成之前,您都會調用hideLoader。您的Ajax庫可能具有「OnComplete」或「OnFinished」回調 - 從那裏調用以下updateStatus。

+0

updateStatus方法激發了jquery ajax調用。我無法在'Onsuccess'回調中隱藏加載器的原因是我需要等待多個調用才能完成。但使用下面給出的'計數器'的方法,我已經有一個解決方法 – 2010-04-14 13:49:26

12

如果您正在進行AJAX編程,您需要將JavaScript視爲基於事件而非過程。在執行第二個呼叫之前,您必須等到第一個呼叫完成。做到這一點的方法是將第二個調用綁定到第一個完成時觸發的回調。不知道更多關於你的AJAX庫的內部工作(希望您使用的是庫)我不能告訴你如何做到這一點,但它可能會是這個樣子:

showLoader(); 

    updateStatus('cron1', function() { 
    updateStatus('cron2', function() { 
     updateStatus('cron3', function() { 
     updateStatus('cronEmail', function() { 
      updateStatus('cronHourly', function() { 
      updateStatus('cronDaily', funciton() { hideLoader(); }) 
      }) 
     }) 
     }) 
    }) 
    }) 
}); 

的想法是,updateStatus採用它的正常參數,加上一個回調函數在完成時執行。傳遞函數將onComplete運行到一個提供這種鉤子的函數是一個相當常見的模式。

更新

如果你使用jQuery,您可以在這裏$.ajax()讀了:http://api.jquery.com/jQuery.ajax/

您的代碼可能看起來是這樣的:

function updateStatus(arg) { 
    // processing 

    $.ajax({ 
    data : /* something */, 
    url : /* something */ 
    }); 

    // processing 
} 

您可以通過修改函數採取回調作爲他們的第二個參數,如下所示:

function updateStatus(arg, onComplete) { 
    $.ajax({ 
    data : /* something */, 
    url : /* something */, 
    complete : onComplete // called when AJAX transaction finishes 
    }); 

}

+0

嗨Meagar,我正在使用jQuery - 什麼是最好的方式來實現呢? – 2010-04-14 13:42:53

5

我們在我們的項目中的一個類似的東西,而我們用一個計數器來解決它。如果您將每次調用的計數器增加到updateStatus,並在AJAX請求的響應函數中減少它(取決於您正在使用的AJAX JavaScript庫)。

一旦計數器達到零,所有AJAX請求都會完成,您可以請致電hideLoader()

這裏有一個例子:

var loadCounter = 0; 

function updateStatuses(){ 
    updateStatus('cron1'); //performs an ajax request to get the status of something 
    updateStatus('cron2'); 
    updateStatus('cron3');  
    updateStatus('cronEmail'); 
    updateStatus('cronHourly'); 
    updateStatus('cronDaily'); 
} 

function updateStatus(what) { 
    loadCounter++; 

    //perform your AJAX call and set the response method to updateStatusCompleted() 
} 

function updateStatusCompleted() { 
    loadCounter--; 
    if (loadCounter <= 0) 
     hideLoader(); //hide the 'loader.gif' in the UI 
} 
+0

嗨Prutswonder,我只是試過這個,它的工作原理!我喜歡解決方案 – 2010-04-14 13:48:07

+3

Ed,如果您喜歡這些解決方案之一,您是否可以通過單擊該人員的答案旁邊的複選框來標記您選擇的最佳解決方案? – 2010-04-14 14:03:39

+0

+1可能比我的解決方案對於如此深度嵌套的回調系列更有效 – meagar 2010-04-14 14:23:42

0

舉動給updateStatus調用另一個函數。以新功能爲目標進行調用setTimeout。

如果你的ajax請求是異步的,你應該有一些東西來跟蹤哪些已完成。每個回調方法都可以爲自己設置一個「完成」標誌,然後檢查它是否是最後一個。如果是,那就叫它hideLoader。

1

正如其他人指出的,你不想做同步操作。擁抱異步,這就是AJAX中的A所代表的。

我只想提一個很好的比較同步v/s異步。你可以閱讀entire post on the GWT forum,我只是包括相關的類比。

試想一下,如果你願意......

你坐在沙發上看電視 ,知道你是出 啤酒,你問你的配偶打倒請 運行到酒鋪和 給你一些。只要你看到 你的配偶走出前門, 你起牀離開沙發和滾滾 進入廚房,並打開冰箱 。令你驚喜的是,沒有 啤酒!

那麼當然沒有啤酒,你的 配偶仍在前往 酒專賣店。你必須等到 [s]他回來之前,你可以期待 有啤酒。

但是,你說你想讓它同步嗎?再想象一下......

...配偶走出門外......現在, 你周圍的整個世界停止,你 不要一口氣,回答 門,或看完你顯示 ,同時[s]他穿過城鎮到 拿起你的啤酒。你只能坐在 那裏沒有動一塊肌肉,和 變藍,直到你失去 意識......醒來後由 急救人員和配偶包圍一些 無限期說哦,嘿嘿,我 拿到你的啤酒。

這就是您堅持要進行同步服務器調用時發生的情況。

10

我認爲,所有你需要做的就是有這個在你的代碼:

async: false, 

所以,你的Ajax調用是這樣的:

jQuery.ajax({ 
      type: "GET", 
      url: "something.html for example", 
      dataType: "html", 
      async: false, 
      context: document.body, 
      success: function(response){ 

       //do stuff here 

      }, 
      error: function() { 
       alert("Sorry, The requested property could not be found."); 
      } 
     }); 

顯然一些這方面的需求,爲XML改變,JSON等,但async: false,是這裏的主要觀點,它告訴JS引擎等待成功呼叫返回(或失敗後),然後繼續。 請記住,這有一個缺點,這就是說,整個頁面變得無反應,直到ajax返回!通常在幾毫秒內,這不是一個大交易,但可能需要更長的時間。

希望這是正確的答案,它可以幫助你:)

+0

這一個伎倆。簡單而簡潔的解決方案。非常感謝。 – darksoulsong 2013-05-04 14:36:04

+0

雖然它的工作,這不是一個真棒的答案。你希望JS是異步的。有很多優點和場景,你希望它去做它自己的事情,並把它關閉整個應用程序是罕見的。 我建議使用承諾庫來代替。這是一個很好的教程。 http://dailyjs.com/2014/02/20/promises-in-detail/ – Codex 2016-12-02 03:56:07

0

一個用於處理所有異步請求的最佳解決方案是「無極」
Promise對象表示異步操作的最終完成(或失敗)。

例子:

let myFirstPromise = new Promise((resolve, reject) => { 
    // We call resolve(...) when what we were doing asynchronously was successful, and reject(...) when it failed. 
    // In this example, we use setTimeout(...) to simulate async code. 
    // In reality, you will probably be using something like XHR or an HTML5 API. 
    setTimeout(function(){ 
    resolve("Success!"); // Yay! Everything went well! 
    }, 250); 
}); 

myFirstPromise.then((successMessage) => { 
    // successMessage is whatever we passed in the resolve(...) function above. 
    // It doesn't have to be a string, but if it is only a succeed message, it probably will be. 
    console.log("Yay! " + successMessage); 
}); 

Promise

如果你有3個異步函數和期待,爲了運行,操作如下:

let FirstPromise = new Promise((resolve, reject) => { 
    FirstPromise.resolve("First!"); 
}); 
let SecondPromise = new Promise((resolve, reject) => { 

}); 
let ThirdPromise = new Promise((resolve, reject) => { 

}); 
FirstPromise.then((successMessage) => { 
    jQuery.ajax({ 
    type: "type", 
    url: "url", 
    success: function(response){ 
     console.log("First! "); 
     SecondPromise.resolve("Second!"); 
    }, 
    error: function() { 
     //handle your error 
    } 
    });   
}); 
SecondPromise.then((successMessage) => { 
    jQuery.ajax({ 
    type: "type", 
    url: "url", 
    success: function(response){ 
     console.log("Second! "); 
     ThirdPromise.resolve("Third!"); 
    }, 
    error: function() { 
     //handle your error 
    } 
    });  
}); 
ThirdPromise.then((successMessage) => { 
    jQuery.ajax({ 
    type: "type", 
    url: "url", 
    success: function(response){ 
     console.log("Third! "); 
    }, 
    error: function() { 
     //handle your error 
    } 
    }); 
}); 

通過這種方法,您可以根據需要處理所有異步操作。