如何從非同步呼叫回傳回應?
P粉668113768
P粉668113768 2023-08-23 12:49:32
0
2
518

如何從發出非同步請求的函數 foo 回傳回應/結果?

我試圖從回調中返回值,並將結果分配給函數內的局部變量並返回該變量,但這些方法都沒有實際返回響應- 它們都返回undefined< /code> 或變量result 的初始值。

接受回呼的非同步函數範例(使用 jQuery 的 ajax 函數):

function foo() { var result; $.ajax({ url: '...', success: function(response) { result = response; // return response; // <- I tried that one as well } }); return result; // It always returns `undefined` }

使用 Node.js 的範例:

function foo() { var result; fs.readFile("path/to/file", function(err, data) { result = data; // return data; // <- I tried that one as well }); return result; // It always returns `undefined` }

使用 Promise 的 then 區塊的範例:

function foo() { var result; fetch(url).then(function(response) { result = response; // return response; // <- I tried that one as well }); return result; // It always returns `undefined` }


P粉668113768
P粉668113768

全部回覆 (2)
P粉334721359

如果您沒有在程式碼中使用 jQuery,這個答案適合您

你的程式碼應該是這樣的:

function foo() { var httpRequest = new XMLHttpRequest(); httpRequest.open('GET', "/echo/json"); httpRequest.send(); return httpRequest.responseText; } var result = foo(); // Always ends up being 'undefined'

菲利克斯·克林做得很好工作為使用 jQuery for AJAX 的人編寫答案,但我決定為不使用 jQuery 的人提供替代方案。

(注意,對於那些使用新的fetchAPI、Angular 或 Promise 的人,我添加了另一個答案如下


你面臨的問題

這是另一個答案中「問題的解釋」的簡短摘要,如果您在閱讀後不確定,請閱讀該答案。

AJAX 中的A代表非同步。這意味著發送請求(或更確切地說接收回應)被從正常執行流程中刪除。在您的範例中,.send code>立即返回,並且在呼叫您作為success回呼傳遞的函數之前執行下一條語句return result;

這表示當您返回時,您定義的偵聽器尚未執行,這表示您傳回的值尚未定義。

這是一個簡單的類比:

function getFive(){ var a; setTimeout(function(){ a=5; },10); return a; }

(小提琴)

#由於a=5部分尚未執行,因此傳回的a值為undefined。 AJAX 的行為是這樣的,您在伺服器有機會告訴您的瀏覽器該值是什麼之前就回傳了該值。

此問題的一個可能的解決方案是重新主動編寫程式碼,告訴您的程式在計算完成後要做什麼。

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傳遞一個要在完成時執行的操作,我們告訴我們的程式碼如何在事件完成時做出反應(例如我們的AJAX 調用,或在本例中是超時) 。

用法是:

getFive(onComplete);

螢幕上會提示「5」。(小提琴)

可能的解決方案

解決這個問題基本上有兩種方法:

  1. 使 AJAX 呼叫同步(我們稱之為 SJAX)。
  2. 重構您的程式碼,以便與回呼一起正常工作。

1。同步 AJAX - 不要這麼做! !

至於同步 AJAX,不要這樣做!Felix 的回答提出了一些令人信服的論點,說明為什麼這是一個壞主意。總而言之,它會凍結用戶的瀏覽器,直到伺服器回傳回應並造成非常糟糕的用戶體驗。以下是來自 MDN 的另一個簡短總結,說明原因:

如果您不得不這樣做,您可以傳遞一個標誌。具體方法如下

var request = new XMLHttpRequest(); request.open('GET', 'yourURL', false); // `false` makes the request synchronous request.send(null); if (request.status === 200) {// That's HTTP for 'ok' console.log(request.responseText); }

2。重組程式碼

讓您的函數接受回呼。在範例程式碼中,可以使foo接受回呼。我們將告訴程式碼當foo完成時如何反應

所以:

var result = foo(); // Code that depends on `result` goes here

變成:

foo(function(result) { // Code that depends on `result` });

這裡我們傳遞了一個匿名函數,但我們也可以輕鬆傳遞對現有函數的引用,使其看起來像:

function myHandler(result) { // Code that depends on `result` } foo(myHandler);

有關如何完成此類回調設計的更多詳細信息,請查看 Felix 的回答。

現在,讓我們定義 foo 本身以進行對應的操作

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(); }

(小提琴)

#現在,我們已經讓foo函數接受一個操作,以便在 AJAX 成功完成時運行。我們可以透過檢查回應狀態是否不是 200 並採取相應措施(建立失敗處理程序等)來進一步擴展此功能。它有效地解決了我們的問題。

如果您仍然很難理解這一點,閱讀 AJAX 取得在 MDN 上開始指南

    P粉642920522

    問題

    Ajax中的A代表非同步。這意味著發送請求(或更確切地說接收回應)被從正常執行流程中刪除。在您的範例中,$.ajax立即返回,並且下一條語句return result;在您作為success回調傳遞的函數之前執行甚至打電話。

    這是一個類比,希望可以讓同步流和非同步流之間的差異更加清晰:

    同步

    想像一下,您打電話給朋友並請他為您查找一些資訊。儘管可能需要一段時間,但您還是在電話旁等待,凝視著太空,直到您的朋友給您所需的答案。

    當您進行包含「正常」程式碼的函數呼叫時,也會發生相同的情況:

    function findItem() { var item; while(item_not_found) { // search } return item; } var item = findItem(); // Do something with item doSomethingElse();

    儘管findItem可能需要很長時間才能執行,但var item = findItem();之後的任何程式碼都必須等待直到該函數返回結果。

    非同步

    您出於同樣的原因再次打電話給您的朋友。但這次你告訴他你很著急,他應該用你的手機回電給你。你掛斷電話,離開家,做你計畫要做的事情。一旦您的朋友回電給您,您就正在處理他提供給您的資訊。

    這正是您發出 Ajax 請求時所發生的情況。

    findItem(function(item) { // Do something with the item }); doSomethingElse();

    不等待回應,而是立即繼續執行,並執行 Ajax 呼叫之後的語句。為了最終獲得回應,您需要提供一個在收到回應後呼叫的函數,即回調(注意到什麼了嗎?回調?)。該呼叫之後的任何語句都會在呼叫回調之前執行。


    解決方案

    擁抱 JavaScript 的非同步特性!雖然某些非同步操作提供同步對應項目(「Ajax」也是如此),但通常不鼓勵使用它們,尤其是在瀏覽器上下文中。

    你問為什麼不好?

    JavaScript 在瀏覽器的 UI 執行緒中執行,任何長時間運行的進程都會鎖定 UI,使其無回應。另外,JavaScript的執行時間是有上限的,瀏覽器會詢問使用者是否繼續執行。

    所有這些都會導致非常糟糕的使用者體驗。用戶將無法判斷一切是否正常。此外,對於網路速度較慢的用戶,效果會更差。

    下面我們將介紹三種不同的解決方案,它們都是相互建構的:

    • 帶有async/await的 Promise(ES2017 ,如果您使用轉譯器或再生器,則可在舊版瀏覽器中使用)
    • 回呼(在節點中流行)
    • 帶有then() 的 Promise(ES2015 ,如果您使用眾多 Promise 庫之一,則可在舊版瀏覽器中使用)

    這三個功能皆可在目前瀏覽器和 Node 7 中使用。


    ES2017 :使用async/await 進行承諾

    2017 年發布的 ECMAScript 版本引入了對非同步函數的語法級支援。使用asyncawait,您可以以「同步風格」編寫非同步。程式碼仍然是異步的,但更容易閱讀/理解。

    async/await建構在 Promise 之上:async函數總是傳回 Promise。await“解開”一個 Promise,並且要么產生 Promise 被解析的值,要么在 Promise 被拒絕時拋出錯誤。

    重要提示:您只能在async函數或await .org/en -US/docs/Web/JavaScript/Guide/Modules" rel="noreferrer">JavaScript 模組。模組外部不支援頂層await,因此您可能必須建立非同步 IIFE (立即呼叫函數表達式)來啟動非同步上下文(如果不使用模組)。

    您可以閱讀有關asyncawait

    這是一個詳細說明上面的延遲函數findItem()的範例:

    // Using 'superagent' which will return a promise. var superagent = require('superagent') // This is isn't declared as `async` because it already returns a promise function delay() { // `delay` returns a promise return new Promise(function(resolve, reject) { // Only `delay` is able to resolve or reject the promise setTimeout(function() { resolve(42); // After 3 seconds, resolve the promise with value 42 }, 3000); }); } async function getAllBooks() { try { // GET a list of book IDs of the current user var bookIDs = await superagent.get('/user/books'); // wait for 3 seconds (just for the sake of this example) await delay(); // GET information about each book return superagent.get('/books/ids='+JSON.stringify(bookIDs)); } catch(error) { // If any of the awaited promises was rejected, this catch block // would catch the rejection reason return null; } } // Start an IIFE to use `await` at the top level (async function(){ let books = await getAllBooks(); console.log(books); })();

    目前瀏覽器node版本支援async/await。您也可以藉助regenerator(或使用 regenerator 的工具)將程式碼轉換為 ES5,以支援較舊的環境,例如Babel)。


    讓函數接受回呼

    回呼是指函數 1 傳遞給函數 2 時。函數 2 可以在函數 1 準備好時呼叫它。在非同步進程的上下文中,只要非同步進程完成,就會呼叫回呼。通常,結果會傳遞給回調。

    在問題的範例中,您可以使foo接受回呼並將其用作success回呼。所以這個

    var result = foo(); // Code that depends on 'result'

    變成了

    foo(function(result) { // Code that depends on 'result' });

    這裡我們定義了「內聯」函數,但您可以傳遞任何函數參考:

    function myCallback(result) { // Code that depends on 'result' } foo(myCallback);

    foo本身定義如下:

    function foo(callback) { $.ajax({ // ... success: callback }); }

    callback將引用我們呼叫時傳遞給foo的函數,並將其傳遞給success。 IE。一旦Ajax請求成功,$.ajax將呼叫callback並將回應傳遞給回呼(可以用result引用,因為這就是我們定義回呼的方式) 。

    您也可以在將回應傳遞給回調之前對其進行處理:

    function foo(callback) { $.ajax({ // ... success: function(response) { // For example, filter the response callback(filtered_response); } }); }

    使用回調編寫程式碼比看起來更容易。畢竟,瀏覽器中的 JavaScript 很大程度上是事件驅動的(DOM 事件)。接收 Ajax 回應只不過是一個事件。 當您必須使用第三方程式碼時可能會出現困難,但大多數問題只需思考應用程式流程即可解決。


    ES2015 :帶有then()的 Promise >

    #Promise API是一個新的ECMAScript 6 (ES2015) 的功能,但它已經具有良好的瀏覽器支援。還有許多函式庫實作了標準 Promises API 並提供了其他方法來簡化非同步函數的使用和組合(例如,藍鳥)。

    Promise 是未來值的容器。當 Promise 收到值(已解決)或被取消(拒絕)時,它會通知所有想要存取該值的「偵聽器」。

    與普通回調相比的優點是它們允許您解耦程式碼並且更容易編寫。

    這是使用 Promise 的範例:

    function delay() { // `delay` returns a promise return new Promise(function(resolve, reject) { // Only `delay` is able to resolve or reject the promise setTimeout(function() { resolve(42); // After 3 seconds, resolve the promise with value 42 }, 3000); }); } delay() .then(function(v) { // `delay` returns a promise console.log(v); // Log the value once it is resolved }) .catch(function(v) { // Or do something else if it is rejected // (it would not happen in this example, since `reject` is not called). });
    .as-console-wrapper { max-height: 100% !important; top: 0; }
      最新下載
      更多>
      網站特效
      網站源碼
      網站素材
      前端模板
      關於我們 免責聲明 Sitemap
      PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!