6 回答

TA貢獻1825條經(jīng)驗 獲得超4個贊
如果您沒有在代碼中使用jQuery,那么這個答案就適合您
你的代碼應(yīng)該是這樣的:
function foo() { var httpRequest = new XMLHttpRequest(); httpRequest.open('GET', "/echo/json"); httpRequest.send(); return httpRequest.responseText;}var result = foo(); // always ends up being 'undefined'
Felix Kling為使用jQuery for AJAX的人寫了一個很好的答案,我決定為那些沒有使用jQuery的人提供替代方案。
你面對的是什么
這是另一個答案的“問題解釋”的簡短摘要,如果您在閱讀本文后不確定,請閱讀。
AJAX中的A代表異步。這意味著發(fā)送請求(或者更確切地說是接收響應(yīng))將從正常執(zhí)行流程中取出。在您的示例中,.send
立即返回,并且在return result;
您作為success
回調(diào)傳遞的函數(shù)被調(diào)用之前執(zhí)行下一個語句。
這意味著當(dāng)您返回時,您定義的偵聽器尚未執(zhí)行,這意味著您返回的值尚未定義。
這是一個簡單的比喻
function getFive(){ var a; setTimeout(function(){ a=5; },10); return a;}
a
返回的值是undefined
因為a=5
零件尚未執(zhí)行。AJAX就像這樣,你在服務(wù)器有機會告訴瀏覽器這個值是什么之前返回值。
一個可能的解決這個問題的代碼重新活躍,告訴你的程序在計算完成后做什么。
function onComplete(a){ // When the code completes, do this alert(a);}function getFive(whenDone){ var a; setTimeout(function(){ a=5; whenDone(a); },10);}
這稱為CPS?;旧?,我們在getFive
完成時傳遞一個動作來執(zhí)行,我們告訴我們的代碼在事件完成時如何反應(yīng)(比如我們的AJAX調(diào)用,或者在這種情況下是超時)。
用法是:
getFive(onComplete);
哪個應(yīng)警告“5”到屏幕。(小提琴)。
可能的解決方案
基本上有兩種解決方法:
使AJAX調(diào)用同步(讓我們稱之為SJAX)。
重構(gòu)代碼以使用回調(diào)正常工作。
1.同步AJAX - 不要這樣做!!
至于同步AJAX,不要這樣做!費利克斯的回答提出了一些令人信服的論據(jù),說明為什么這是一個壞主意。總而言之,它會凍結(jié)用戶的瀏覽器,直到服務(wù)器返回響應(yīng)并創(chuàng)建非常糟糕的用戶體驗。以下是MDN對于其原因的另一個簡短摘要:
XMLHttpRequest支持同步和異步通信。但是,一般而言,出于性能原因,異步請求應(yīng)優(yōu)先于同步請求。
簡而言之,同步請求會阻止代碼的執(zhí)行......這可能會導(dǎo)致嚴(yán)重的問題......
如果你必須這樣做,你可以傳遞一個標(biāo)志:這是如何:
var request = new XMLHttpRequest();request.open('GET', 'yourURL', false); // `false` makes the request synchronousrequest.send(null);if (request.status === 200) {// That's HTTP for 'ok' console.log(request.responseText);}
2.重組代碼
讓你的函數(shù)接受回調(diào)。在示例foo
中,可以使代碼接受回調(diào)。我們將告訴我們的代碼在完成時如何反應(yīng)foo
。
所以:
var result = foo();// code that depends on `result` goes here
變?yōu)椋?/p>
foo(function(result) { // code that depends on `result`});
這里我們傳遞了一個匿名函數(shù),但我們可以輕松地將引用傳遞給現(xiàn)有函數(shù),使其看起來像:
function myHandler(result) { // code that depends on `result`}foo(myHandler);
有關(guān)如何完成此類回調(diào)設(shè)計的更多詳細(xì)信息,請查看Felix的答案。
現(xiàn)在,讓我們定義foo自己采取相應(yīng)的行動
function foo(callback) { var httpRequest = new XMLHttpRequest(); httpRequest.onload = function(){ // when the request is loaded callback(httpRequest.responseText);// we're calling our method }; httpRequest.open('GET', "/echo/json"); httpRequest.send();}
我們現(xiàn)在已經(jīng)讓我們的foo函數(shù)接受了一個在AJAX成功完成時運行的動作,我們可以通過檢查響應(yīng)狀態(tài)是否為200并進行相應(yīng)的操作來進一步擴展它(創(chuàng)建一個失敗處理程序等)。有效解決我們的問題。
如果您仍然很難理解這一點,請閱讀 MDN 的AJAX入門指南。

TA貢獻1852條經(jīng)驗 獲得超7個贊
XMLHttpRequest 2(首先閱讀 Benjamin Gruenbaum和 Felix Kling的答案)
如果你不使用jQuery并想要一個很好的簡短的XMLHttpRequest 2,它適用于現(xiàn)代瀏覽器,也適用于移動瀏覽器,我建議用這種方式:
function ajax(a, b, c){ // URL, callback, just a placeholder c = new XMLHttpRequest; c.open('GET', a); c.onload = b; c.send()}
如你看到的:
它比列出的所有其他功能都短。
回調(diào)是直接設(shè)置的(因此沒有額外的不必要的閉包)。
它使用新的onload(因此您不必檢查readystate && status)
還有其他一些我不記得的情況會讓XMLHttpRequest 1變得煩人。
有兩種方法可以獲得此Ajax調(diào)用的響應(yīng)(三種使用XMLHttpRequest var名稱):
最簡單的:
this.response
或者,如果由于某種原因你bind()
回調(diào)到一個類:
e.target.response
例:
function callback(e){ console.log(this.response);}ajax('URL', callback);
或者(上面的一個是更好的匿名函數(shù)總是一個問題):
ajax('URL', function(e){console.log(this.response)});
沒什么比這更容易
現(xiàn)在有些人可能會說最好使用onreadystatechange或甚至XMLHttpRequest變量名。那是錯的。
它支持所有*現(xiàn)代瀏覽器。我可以確認(rèn),因為我使用這種方法,因為XMLHttpRequest 2存在。在我使用的所有瀏覽器上,我從未遇到任何類型的問題。
onreadystatechange僅在您希望獲取狀態(tài)2的標(biāo)頭時才有用。
使用XMLHttpRequest
變量名是另一個大錯誤,因為你需要在onload / oreadystatechange閉包內(nèi)執(zhí)行回調(diào),否則你就丟失了它。
現(xiàn)在,如果您想使用post和FormData更復(fù)雜的東西,您可以輕松擴展此功能:
function x(a, b, e, d, c){ // URL, callback, method, formdata or {key:val},placeholder c = new XMLHttpRequest; c.open(e||'get', a); c.onload = b; c.send(d||null)}
再一次......這是一個非常短的功能,但它確實得到和發(fā)布。
用法示例:
x(url, callback); // By default it's get so no need to setx(url, callback, 'post', {'key': 'val'}); // No need to set post data
或者傳遞一個完整的表單元素(document.getElementsByTagName('form')[0]
):
var fd = new FormData(form);x(url, callback, 'post', fd);
或者設(shè)置一些自定義值:
var fd = new FormData();fd.append('key', 'val')x(url, callback, 'post', fd);
如你所見,我沒有實現(xiàn)同步...這是一件壞事。
話雖如此......為什么不這么簡單呢?
正如評論中所提到的,使用error && synchronous確實完全打破了答案的要點。哪個是以正確方式使用Ajax的簡短方法?
錯誤處理程序
function x(a, b, e, d, c){ // URL, callback, method, formdata or {key:val}, placeholder c = new XMLHttpRequest; c.open(e||'get', a); c.onload = b; c.onerror = error; c.send(d||null)}function error(e){ console.log('--Error--', this.type); console.log('this: ', this); console.log('Event: ', e)}function displayAjax(e){ console.log(e, this);}x('WRONGURL', displayAjax);
在上面的腳本中,您有一個靜態(tài)定義的錯誤處理程序,因此它不會危及該功能。錯誤處理程序也可用于其他功能。
但要真正解決錯誤,唯一的方法是寫一個錯誤的URL,在這種情況下每個瀏覽器都會拋出一個錯誤。
如果您設(shè)置自定義標(biāo)頭,將responseType設(shè)置為blob數(shù)組緩沖區(qū)或其他任何內(nèi)容,則錯誤處理程序可能很有用...
即使你傳遞'POSTAPAPAP'作為方法它也不會拋出錯誤。
即使你將'fdggdgilfdghfldj'作為formdata傳遞它也不會拋出錯誤。
在第一種情況下,錯誤在displayAjax()
under this.statusText
as中Method not Allowed
。
在第二種情況下,它只是起作用。如果您傳遞了正確的帖子數(shù)據(jù),則必須在服務(wù)器端進行檢查。
跨域不允許自動拋出錯誤。
在錯誤響應(yīng)中,沒有錯誤代碼。
只有this.type
哪個被設(shè)置為錯誤。
如果您完全無法控制錯誤,為什么要添加錯誤處理程序?大多數(shù)錯誤都在回調(diào)函數(shù)中返回displayAjax()
。
因此:如果您能夠正確復(fù)制和粘貼URL,則無需進行錯誤檢查。;)
PS:作為我寫的第一個測試x('x',displayAjax)......,它完全得到了回應(yīng)...... ??? 所以我檢查了HTML所在的文件夾,并且有一個名為'x.xml'的文件。因此,即使您忘記了文件的擴展名,XMLHttpRequest 2也會找到它。我好意思
同步讀取文件
不要那樣做。
如果你想阻止瀏覽器一段時間加載一個漂亮的大.txt
文件同步。
function omg(a, c){ // URL c = new XMLHttpRequest; c.open('GET', a, true); c.send(); return c; // Or c.response}
現(xiàn)在你可以做到
var res = omg('thisIsGonnaBlockThePage.txt');
沒有其他方法可以以非異步方式執(zhí)行此操作。(是的,使用setTimeout循環(huán)......但是認(rèn)真嗎?)
另一點是......如果你使用API或只是你自己的列表文件或者你總是為每個請求使用不同的函數(shù)...
只有當(dāng)你有一個頁面,你總是加載相同的XML / JSON或任何你只需要一個函數(shù)。在這種情況下,修改一點Ajax函數(shù)并用您的特殊函數(shù)替換b。
以上功能僅供基本使用。
如果你想擴展功能......
是的你可以。
我使用了很多API,我在每個HTML頁面中集成的第一個函數(shù)之一是這個答案中的第一個Ajax函數(shù),只有GET ...
但是你可以用XMLHttpRequest 2做很多事情:
我創(chuàng)建了一個下載管理器(使用范圍,包括簡歷,文件讀取器,文件系統(tǒng)),使用畫布的各種圖像調(diào)整器轉(zhuǎn)換器,使用base64images填充Web SQL數(shù)據(jù)庫等等......但在這些情況下,您應(yīng)該只創(chuàng)建一個函數(shù)目的...有時你需要一個blob,數(shù)組緩沖區(qū),你可以設(shè)置標(biāo)題,覆蓋mimetype,還有更多......
但這里的問題是如何返回Ajax響應(yīng)...(我添加了一個簡單的方法。)

TA貢獻1876條經(jīng)驗 獲得超5個贊
您正在使用Ajax。我們的想法不是讓它返回任何東西,而是將數(shù)據(jù)交給稱為回調(diào)函數(shù)的東西,該函數(shù)處理數(shù)據(jù)。
那是:
function handleData( responseData ) { // Do what you want with the data console.log(responseData);}$.ajax({ url: "hi.php", ... success: function ( data, status, XHR ) { handleData(data); }});
在提交處理程序中返回任何內(nèi)容都不會執(zhí)行任何操作。您必須切換數(shù)據(jù),或者直接在成功函數(shù)內(nèi)執(zhí)行您想要的操作。

TA貢獻1818條經(jīng)驗 獲得超8個贊
最簡單的解決方案是創(chuàng)建一個JavaScript函數(shù)并調(diào)用它來進行Ajax success
回調(diào)。
function callServerAsync(){ $.ajax({ url: '...', success: function(response) { successCallback(response); } });}function successCallback(responseObj){ // Do something like read the response and show data alert(JSON.stringify(responseObj)); // Only applicable to JSON response}function foo(callback) { $.ajax({ url: '...', success: function(response) { return callback(null, response); } });}var result = foo(function(err, result){ if (!err) console.log(result); });
添加回答
舉報