如何从异步调用返回响应?

问题描述:

如何从发出异步请求的函数 foo 返回响应/结果?

我正在尝试从回调中返回值,并将结果分配给函数内部的局部变量并返回该变量,但这些方法都没有真正返回响应——它们都返回 undefined 或任何初始值变量 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`
}

使用承诺的 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`
}

解决方案1:

huntsbot.com – 高效赚钱,自由工作

→ 有关不同示例的异步行为的更一般解释,请参阅为什么我的变量在我在函数内部修改后没有改变? - 异步代码参考 → 如果您已经了解问题,请跳至下面可能的解决方案。

问题

Ajax 中的 A 代表 asynchronous。这意味着发送请求(或者更确切地说是接收响应)从正常的执行流程中取出。在您的示例中,$.ajax 立即返回,并且在您作为 success 回调传递的函数甚至被调用之前执行下一条语句 return result;。

这是一个类比,它有望使同步流和异步流之间的区别更加清晰:

同步

想象一下,您给朋友打了个电话,请他为您查找一些东西。虽然这可能需要一段时间,但您在电话上等待并凝视空间,直到您的朋友给您所需的答案。

当您进行包含“正常”代码的函数调用时,也会发生同样的情况:

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() 的承诺(ES2015+,如果您使用众多承诺库之一,则可在较旧的浏览器中使用)

所有这三个都在当前浏览器和节点 7+ 中可用。

ES2017+:异步/等待的承诺

2017 年发布的 ECMAScript 版本为异步函数引入了语法级别的支持。在 async 和 await 的帮助下,您可以以“同步风格”编写异步。代码仍然是异步的,但更容易阅读/理解。

async/await 建立在 Promise 之上:async 函数总是返回一个 Promise。 await“解包”一个promise,或者导致promise 被解析的值,或者如果promise 被拒绝则抛出一个错误。

重要提示:您只能在 async 函数或 JavaScript module 中使用 await。模块外不支持顶级 await,因此如果不使用模块,您可能必须创建异步 IIFE (Immediately Invoked Function Expression) 来启动 async 上下文。

您可以在 MDN 上阅读有关 async 和 await 的更多信息。

下面是一个详细说明上述 delay 函数 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);
})();

当前的 browser 和 node 版本支持 async/await。您还可以借助 regenerator(或使用再生器的工具,例如 Babel)将代码转换为 ES5 来支持旧环境。

让函数接受回调

回调是将函数 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。即,一旦 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 API 是 ECMAScript 6 (ES2015) 的一个新特性,但它已经具有很好的 browser support。还有许多库实现了标准 Promises API 并提供了额外的方法来简化异步函数的使用和组合(例如,bluebird)。

Promise 是未来值的容器。当 Promise 接收到该值(已解决)或被取消(拒绝)时,它会通知所有想要访问该值的“侦听器”。

与普通回调相比,它们的优势在于它们允许您解耦代码并且它们更易于编写。

下面是一个使用 Promise 的例子:

function delay() { // delay 返回一个 promise return new Promise(function(resolve, reject) { // 只有 delay 能够解决或拒绝这个 promise setTimeout(function() { resolve(42); / / 3秒后,用值42解决promise }, 3000); }); } delay() .then(function(v) { // delay 返回一个 promise console.log(v); // 解析后记录值 }) .catch(function(v) { // 或者做如果它被拒绝 // (在这个例子中它不会发生,因为 reject 没有被调用)。 }); .as-console-wrapper { max-height: 100% !important;顶部:0; }

应用到我们的 Ajax 调用中,我们可以使用这样的 Promise:

函数 ajax(url) { return new Promise(function(resolve, reject) { var xhr = new XMLHttpRequest(); xhr.onload = function() { resolve(this.responseText); }; xhr.onerror = reject; xhr. open(‘GET’, url); xhr.send(); }); } ajax(“https://jsonplaceholder.typicode.com/todos/1”) .then(function(result) { console.log(result); // 代码取决于结果 }) .catch(function() { / / 发生错误 }); .as-console-wrapper { max-height: 100% !important;顶部:0; }

描述承诺提供的所有优势超出了这个答案的范围,但如果你编写新代码,你应该认真考虑它们。它们为您的代码提供了很好的抽象和分离。

有关承诺的更多信息:HTML5 rocks - JavaScript Promises。

旁注:jQuery 的延迟对象

Deferred objects 是 jQuery 的 Promise 自定义实现(在 Promise API 标准化之前)。它们的行为几乎像 Promise,但暴露了稍微不同的 API。

jQuery 的每个 Ajax 方法都已经返回了一个“延迟对象”(实际上是一个延迟对象的承诺),您可以从函数中返回它:

function ajax() {
    return $.ajax(...);
}

ajax().done(function(result) {
    // Code depending on result
}).fail(function() {
    // An error occurred
});

旁注:承诺陷阱

请记住,承诺和延迟对象只是未来值的容器,它们不是值本身。例如,假设您有以下内容:

function checkPassword() {
    return $.ajax({
        url: '/password',
        data: {
            username: $('#username').val(),
            password: $('#password').val()
        },
        type: 'POST',
        dataType: 'json'
    });
}

if (checkPassword()) {
    // Tell the user they're logged in
}

此代码误解了上述异步问题。具体来说,$.ajax() 在检查服务器上的“/password”页面时不会冻结代码 - 它会向服务器发送请求,并在等待时立即返回 jQuery Ajax Deferred 对象,而不是来自服务器。这意味着 if 语句将始终获取此 Deferred 对象,将其视为 true,并像用户登录一样继续进行。不好。

但修复很简单:

checkPassword()
.done(function(r) {
    if (r) {
        // Tell the user they're logged in
    } else {
        // Tell the user their password was bad
    }
})
.fail(function(x) {
    // Tell the user something bad happened
});

不推荐:同步“Ajax”调用

正如我所提到的,一些(!)异步操作具有同步对应物。我不提倡使用它们,但为了完整起见,以下是执行同步调用的方式:

没有 jQuery

如果您直接使用 XMLHttpRequest 对象,请将 false 作为第三个参数传递给 .open。

jQuery

如果您使用 jQuery,您可以将 async 选项设置为 false。请注意,自 jQuery 1.8 起,此选项已弃用。然后,您仍然可以使用 success 回调或访问 jqXHR object 的 responseText 属性:

function foo() {
    var jqXHR = $.ajax({
        //...
        async: false
    });
    return jqXHR.responseText;
}

如果您使用任何其他 jQuery Ajax 方法,例如 . g e t 、 .get、 .get.getJSON 等,则必须将其更改为 $.ajax(因为您只能将配置参数传递给 $.ajax)。

请注意!无法发出同步 JSONP 请求。 JSONP 就其本质而言始终是异步的(甚至不考虑此选项的另一个原因)。

@Pommy:如果你想使用 jQuery,你必须包含它。请参阅docs.jquery.com/Tutorials:Getting_Started_with_jQuery。

在解决方案 1,子 jQuery 中,我无法理解这一行:If you use any other jQuery AJAX method, such as $.get, $.getJSON, etc., you have them to $.ajax.(是的,我意识到在这种情况下我的昵称有点讽刺)

@gibberish:嗯,我不知道如何使它更清楚。您是否看到如何调用 foo 并将函数传递给它 (foo(function(result) {....});)? result 在这个函数内部使用,是 Ajax 请求的响应。要引用此函数,foo 的第一个参数称为 callback 并分配给 success 而不是匿名函数。因此,$.ajax 将在请求成功时调用 callback。我试着解释一下。

这个问题的聊天已经死了,所以我不确定在哪里提出概述的更改,但我建议:1)将同步部分更改为简单讨论为什么它不好,没有代码示例说明如何做到这一点。 2)删除/合并回调示例以仅显示更灵活的延迟方法,我认为对于那些学习 Javascript 的人来说,这也可能更容易理解。

@Jessi:我认为您误解了答案的那一部分。如果您希望 Ajax 请求是同步的,则不能使用 $.getJSON。但是,您不应该希望请求是同步的,因此这不适用。您应该使用回调或承诺来处理响应,正如答案前面所述。

解决方案2:

huntsbot.com – 程序员副业首选,一站式外包任务、远程工作、创意产品分享订阅平台。

如果您没有在代码中使用 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'

Felix Kling did a fine job 为使用 jQuery for AJAX 的人写一个答案,但我决定为不使用 jQuery 的人提供替代方案。

(Note, for those using the new fetch API, Angular or promises I’ve added another answer below)

你所面临的

这是另一个答案中“问题解释”的简短摘要,如果您在阅读后不确定,请阅读。

AJAX 中的 A 代表 asynchronous。这意味着发送请求(或者更确切地说是接收响应)从正常的执行流程中取出。在您的示例中,.send 立即返回,并且在您作为 success 回调传递的函数甚至被调用之前执行下一条语句 return result;。

这意味着当您返回时,您定义的侦听器尚未执行,这意味着您返回的值尚未定义。

这是一个简单的类比:

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

(Fiddle)

返回的 a 的值为 undefined,因为 a=5 部分尚未执行。 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”。 (Fiddle)。

可能的解决方案

基本上有两种方法可以解决这个问题:

使 AJAX 调用同步(我们称之为 SJAX)。重构您的代码以与回调一起正常工作。

  1. 同步 AJAX - 不要这样做!

至于同步AJAX,千万别搞! Felix 的回答引发了一些令人信服的论点,说明为什么这是一个坏主意。总而言之,它将冻结用户的浏览器,直到服务器返回响应并创建非常糟糕的用户体验。这是从 MDN 摘录的另一个简短摘要,说明了原因:

XMLHttpRequest 支持同步和异步通信。然而,一般来说,出于性能原因,异步请求应优先于同步请求。简而言之,同步请求会阻塞代码的执行……这可能会导致严重的问题……

如果您有这样做,您可以传递一个标志。 Here is how:

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

(fiddle)

我们现在让我们的 foo 函数接受在 AJAX 成功完成时运行的操作。我们可以通过检查响应状态是否不是 200 并采取相应措施(创建一个失败处理程序等)来进一步扩展它。它有效地解决了我们的问题。

如果您仍然难以理解这一点,请访问 MDN read the AJAX getting started guide。

“同步请求会阻塞代码的执行并可能泄漏内存和事件”同步请求如何泄漏内存?

解决方案3:

保持自己快人一步,享受全网独家提供的一站式外包任务、远程工作、创意产品订阅服务–huntsbot.com

XMLHttpRequest 2(首先,阅读 Benjamin Gruenbaum 和 Felix Kling 的答案)

如果你不使用 jQuery 并且想要一个在现代浏览器和移动浏览器中工作的漂亮的简短 XMLHttpRequest 2,我建议以这种方式使用它:

function ajax(a, b, c){ // URL, callback, just a placeholder
  c = new XMLHttpRequest;
  c.open('GET', a);
  c.onload = b;
  c.send()
}

如你看到的:

它比列出的所有其他功能都短。回调是直接设置的(因此没有额外的不必要的闭包)。它使用新的 onload(因此您不必检查 readystate && 状态) 还有一些我不记得的其他情况使 XMLHttpRequest 1 烦人。

有两种方法可以获取此 Ajax 调用的响应(三种使用 XMLHttpRequest var 名称):

最简单的:

this.response

或者,如果由于某种原因您bind() 回调某个类:

e.target.response

例子:

function callback(e){
  console.log(this.response);
}
ajax('URL', callback);

或者(上面一个更好的匿名函数总是一个问题):

ajax('URL', function(e){console.log(this.response)});

没有什么比这更容易的了。

现在可能有人会说最好使用onreadystatechange 甚至XMLHttpRequest 变量名。那是错误的。

查看XMLHttpRequest advanced features。

它支持所有*现代浏览器。而且我可以确认,自从 XMLHttpRequest 2 创建以来,我一直在使用这种方法。在我使用的任何浏览器中,我从来没有遇到过任何类型的问题。

onreadystatechange 仅在您想要获取状态 2 的标头时才有用。

使用 XMLHttpRequest 变量名是另一个大错误,因为您需要在 onload/oreadystatechange 闭包内执行回调,否则您会丢失它。

现在,如果您想要使用 POST 和 FormData 进行更复杂的操作,您可以轻松扩展此函数:

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

再说一次……这是一个非常短的函数,但它会执行 GET 和 POST。

使用示例:

x(url, callback); // By default it's GET so no need to set
x(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);

或者设置一些自定义值:

var fd = new FormData();
fd.append('key', 'val')
x(url, callback, 'post', fd);

如您所见,我没有实现同步…这是一件坏事。

话虽如此…为什么我们不做简单的方法呢?

正如评论中提到的,使用错误 && 同步确实完全打破了答案的重点。哪个是正确使用 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);

在上面的脚本中,您有一个静态定义的错误处理程序,因此它不会损害功能。错误处理程序也可以用于其他功能。

但要真正摆脱错误,唯一的方法是编写错误的 URL,在这种情况下,每个浏览器都会抛出错误。

如果您设置自定义标头、将 responseType 设置为 blob 数组缓冲区或其他任何内容,错误处理程序可能会很有用…

即使您将 ‘POSTAPAPAP’ 作为方法传递,它也不会引发错误。

即使您将 ‘fdggdgilfdghfldj’ 作为 formdata 传递,它也不会引发错误。

在第一种情况下,错误位于 this.statusText 下的 displayAjax() 内,即 Method not Allowed。

在第二种情况下,它可以正常工作。如果您传递了正确的发布数据,您必须在服务器端进行检查。

不允许跨域自动抛出错误。

在错误响应中,没有任何错误代码。

只有 this.type 设置为 error。

如果您完全无法控制错误,为什么还要添加错误处理程序?大多数错误都在回调函数 displayAjax() 中返回。

所以:如果您能够正确复制和粘贴 URL,则不需要进行错误检查。 😉

PS:作为第一个测试,我写了 x(‘x’, displayAjax)…,它完全得到了响应…???所以我检查了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
}

现在你可以做

 var res = omg('thisIsGonnaBlockThePage.txt');

没有其他方法可以以非异步方式执行此操作。 (是的,使用 setTimeout 循环…但认真吗?)

另一点是…如果您使用 API 或只是您自己的列表文件或任何您总是为每个请求使用不同功能的东西…

仅当您有一个页面始终加载相同的 XML/JSON 或任何您只需要一个功能的页面时。在这种情况下,稍微修改 Ajax 函数并将 b 替换为您的特殊函数。

以上功能为基本使用。

如果要扩展功能…

是的你可以。

我使用了很多 API,我集成到每个 HTML 页面的第一个函数是这个答案中的第一个 Ajax 函数,只有 GET …

但是你可以用 XMLHttpRequest 2 做很多事情:

我制作了一个下载管理器(使用简历、文件阅读器和文件系统两侧的范围)、使用画布的各种图像大小调整器转换器、使用 base64images 填充 Web SQL 数据库等等…

但在这些情况下,您应该只为此目的创建一个函数……有时您需要一个 blob、数组缓冲区、您可以设置标题、覆盖 mimetype 等等……

但这里的问题是如何返回 Ajax 响应…(我添加了一个简单的方法。)

虽然这个答案很好(而且我们都喜欢 XHR2 并且发布文件数据和多部分数据非常棒) - 这显示了使用 JavaScript 发布 XHR 的语法糖 - 你可能想把它放在博客文章中(我想要)甚至在图书馆中(不确定名称 x、ajax 或 xhr 可能更好:))。我看不到它如何解决从 AJAX 调用返回响应的问题。 (有人仍然可以做var res = x("url"),但不明白为什么它不起作用;))。附带说明 - 如果您从该方法返回 c 会很酷,这样用户就可以挂钩 error 等。

2.ajax is meant to be async.. so NO var res=x('url').. 这就是这个问题和答案的全部意义 :)

@cocco所以您在SO答案中编写了误导性,不可读的代码以节省一些击键?请不要那样做。

解决方案4:

与HuntsBot一起,探索全球自由职业机会–huntsbot.com

如果您使用的是 Promise,那么这个答案适合您。

这意味着 AngularJS、jQuery(带延迟)、本机 XHR 的替换(提取)、Ember.js、Backbone.js 的保存或任何返回承诺的 Node.js 库。

你的代码应该是这样的:

function foo() {
    var data;
    // Or $.get(...).then, or request(...).then, or query(...).then
    fetch("/echo/json").then(function(response){
        data = response.json();
    });
    return data;
}

var result = foo(); // 'result' is always undefined no matter what.

Felix Kling did a fine job 为使用带有 Ajax 回调的 jQuery 的人编写答案。我有一个原生 XHR 的答案。这个答案适用于前端或后端的 Promise 的通用用法。

核心问题

浏览器和服务器上的 JavaScript 并发模型与 Node.js/io.js 是异步和反应式的。

每当您调用返回 Promise 的方法时,then 处理程序总是 异步执行 - 即,在它们下方不在 .then 中的代码之后处理程序。

这意味着当您返回 data 时,您定义的 then 处理程序尚未执行。这反过来意味着您返回的值没有及时设置为正确的值。

这是这个问题的一个简单类比:

函数 getFive(){ var 数据; setTimeout(function(){ // 设置未来一秒的定时器 data = 5; // 一秒后,执行此操作 }, 1000);返回数据; } document.body.innerHTML = getFive(); // 这里是 undefined 而不是 5

data 的值为 undefined,因为 data = 5 部分尚未执行。它可能会在一秒钟内执行,但到那时它与返回值无关。

由于操作尚未发生(Ajax、服务器调用、I/O 和计时器),您在请求有机会告诉您的代码该值是什么之前返回该值。

这个问题的一种可能的解决方案是重新编写代码,告诉你的程序在计算完成后要做什么。 Promise 通过本质上是临时的(时间敏感的)来积极地实现这一点。

快速回顾承诺

Promise 是一个随时间变化的值。 Promise 有状态。它们以没有价值的待处理开始,并且可以解决:

完成意味着计算成功完成。

拒绝意味着计算失败。

一个promise 只能改变状态一次,之后它将永远保持在同一个状态。您可以将 then 处理程序附加到 Promise 以提取它们的值并处理错误。 then 处理程序允许 chaining 次调用。 Promise 由 using APIs that return them 创建。例如,更现代的 Ajax 替换 fetch 或 jQuery 的 $.get 返回承诺。

当我们在一个 Promise 上调用 .then 并从中返回一些东西时 - 我们得到一个 已处理值 的 Promise。如果我们兑现另一个承诺,我们会得到惊人的东西,但让我们抓住我们的马。

带着承诺

让我们看看如何用 Promise 解决上述问题。首先,让我们通过使用 Promise constructor 创建延迟函数来展示我们对上述承诺状态的理解:

function delay(ms){ // Takes amount of milliseconds
    // Returns a new promise
    return new Promise(function(resolve, reject){
        setTimeout(function(){ // When the time is up,
            resolve(); // change the promise to the fulfilled state
        }, ms);
    });
}

现在,在我们 converted setTimeout 使用 Promise 之后,我们可以使用 then 使其计数:

function delay(ms){ // 花费毫秒数 // 返回一个新的 promise return new Promise(function(resolve, reject){ setTimeout(function(){ // 时间到了,resolve(); // 改变对已完成状态的承诺 }, ms); }); } function getFive(){ // 我们正在返回承诺。请记住,promise 是我们的值的包装器 return delay(100).then(function(){ // 当 promise 准备好时,返回 5; // 返回值 5。Promise 都是关于返回值 }) } / / 我们_必须_在调用站点中像这样包装它,我们无法访问纯值 getFive().then(function(five){ document.body.innerHTML = 五; });

基本上,我们不是返回一个由于并发模型而无法执行的 value - 我们返回一个 wrapper 以获取我们可以 unwrap< /em> 与 then。它就像一个可以用 then 打开的盒子。

应用这个

这与您的原始 API 调用相同,您可以:

function foo() {
    // RETURN the promise
    return fetch("/echo/json").then(function(response){
        return response.json(); // Process it inside the `then`
    });
}

foo().then(function(response){
    // Access the value inside the `then`
})

所以这同样有效。我们已经知道我们不能从已经异步的调用中返回值,但是我们可以使用 Promise 并链接它们来执行处理。我们现在知道如何从异步调用返回响应。

ES2015 (ES6)

ES6 引入了 generators ,它们是可以在中间返回然后恢复它们所在点的函数。这通常对序列很有用,例如:

function* foo(){ // Notice the star. This is ES6, so new browsers, Nodes.js, and io.js only
    yield 1;
    yield 2;
    while(true) yield 3;
}

是一个函数,它在可以迭代的序列 1,2,3,3,3,3,… 上返回一个 iterator。虽然这本身很有趣并且为很多可能性打开了空间,但有一个特别有趣的案例。

如果我们生成的序列是一系列动作而不是数字 - 我们可以在产生动作时暂停函数并在恢复函数之前等待它。因此,我们需要一系列未来值,而不是数字序列——即:promise。

这有点棘手,但非常强大的技巧让我们以同步的方式编写异步代码。有几个“跑步者”可以为你做这件事。写一个是短短的几行代码,但这超出了这个答案的范围。我将在这里使用 Bluebird 的 Promise.coroutine,但还有其他包装器,例如 co 或 Q.async。

var foo = coroutine(function*(){
    var data = yield fetch("/echo/json"); // Notice the yield
    // The code here only executes _after_ the request is done
    return data.json(); // 'data' is defined
});

此方法本身返回一个 Promise,我们可以从其他协程中使用它。例如:

var main = coroutine(function*(){
   var bar = yield foo(); // Wait our earlier coroutine. It returns a promise
   // The server call is done here, and the code below executes when done
   var baz = yield fetch("/api/users/" + bar.userid); // Depends on foo's result
   console.log(baz); // Runs after both requests are done
});
main();

ES2016 (ES7)

在 ES7 中,这被进一步标准化。目前有几个提案,但您可以在所有提案中await承诺。通过添加 async 和 await 关键字,这只是上面 ES6 提案的“糖”(更好的语法)。制作上面的例子:

async function foo(){
    var data = await fetch("/echo/json"); // Notice the await
    // code here only executes _after_ the request is done
    return data.json(); // 'data' is defined
}

它仍然返回一个相同的承诺:)

解决方案5:

huntsbot.com汇聚了国内外优秀的初创产品创意,可按收入、分类等筛选,希望这些产品与实践经验能给您带来灵感。

您错误地使用了 Ajax。这个想法不是让它返回任何东西,而是将数据交给一个叫做回调函数的东西,它处理数据。

那是:

function handleData( responseData ) {

    // Do what you want with the data
    console.log(responseData);
}

$.ajax({
    url: "hi.php",
    ...
    success: function ( data, status, XHR ) {
        handleData(data);
    }
});

在提交处理程序中返回任何内容都不会做任何事情。相反,你必须要么交出数据,要么直接在成功函数中做你想做的事。

这个答案是完全语义的......你的成功方法只是回调中的回调。您可以只拥有 success: handleData 并且它会起作用。

解决方案6:

HuntsBot周刊–不定时分享成功产品案例,学习他们如何成功建立自己的副业–huntsbot.com

我会用一个看起来很可怕的手绘漫画来回答。第二张图片是您的代码示例中 result 为 undefined 的原因。

https://i.imgur.com/v5ksbBC.jpg

一张图片值一千字,A 人 - 询问 B 人的详细信息来修理他的车,然后人 B - 进行 Ajax 调用并等待服务器的响应以获取汽车修理细节,当收到响应时,Ajax Success 函数调用此人B 函数并将响应作为参数传递给它,人 A 接收答案。

如果您在每个图像中添加代码行来说明概念,那就太好了。

与此同时,开车的人被困在路边。他要求汽车修好后再继续。他现在一个人在路边等着……他宁愿打电话等状态变化,但机械师不会这样做……机械师说他必须继续工作,不能简单地挂电话。机械师保证会尽快给他回电话。大约 4 小时后,这个人放弃了,打电话给优步。 - 超时示例。

但是使用回调函数,我觉得最后一帧左边的人被迫不给对方他们的电话号码。相反,他们必须告诉对方,“这就是我想用电话里那个家伙的信息做的所有事情。做所有这些事情,永远不要告诉我。”我错过了什么?

@FingLixon 无论如何这都不是一部完美的漫画:-D。第二张图片应该说明当您尝试过早(在回调发生之前)读取值时会发生什么。第三张图片说明了设置回调方法:左边的人基本上是回调处理程序:一旦信息可用,他将被调用,然后可以随心所欲地使用它。我现在认为在这部漫画中打两个电话是个坏主意:打给商店的电话和打给左边那个人的电话。我应该简化一下,对此感到抱歉。

解决方案7:

一个优秀的自由职业者,应该有对需求敏感和精准需求捕获的能力,而huntsbot.com提供了这个机会

最简单的解决方案是创建一个 JavaScript 函数并为 Ajax success 回调调用它。

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

我不知道是谁投了反对票。但这是一项有效的工作,实际上我使用这种方法来创建整个应用程序。 jquery.ajax 不返回数据,因此最好使用上述方法。如果它是错误的,那么请解释并提出更好的方法来做到这一点。

对不起,我忘了发表评论(我通常这样做!)。我投了反对票。否决票并不表示事实正确或缺乏,它们表示在上下文中的有用性或缺乏。鉴于 Felix 已经更详细地解释了这一点,我认为您的答案没有用。附带说明一下,如果响应是 JSON,为什么要对响应进行字符串化?

好的.. @Benjamin 我使用 stringify 将 JSON 对象转换为字符串。并感谢您澄清您的观点。将记住发布更详细的答案。

解决方案8:

huntsbot.com聚合了超过10+全球外包任务平台的外包需求,寻找外包任务与机会变的简单与高效。

角 1

使用 AngularJS 的人可以使用 promises 处理这种情况。

Here 它说,

Promise 可用于取消嵌套异步函数,并允许将多个函数链接在一起。

您还可以找到一个很好的解释 here。

下面提到的 documentation 中的示例。

  promiseB = promiseA.then(
    function onSuccess(result) {
      return result + 1;
    }
    ,function onError(err) {
      // Handle error
    }
  );

 // promiseB will be resolved immediately after promiseA is resolved
 // and its value will be the result of promiseA incremented by 1.

Angular 2 及更高版本

在 Angular 2 中查看以下示例,但它的 recommended 将 observables 与 Angular 2 一起使用。

 search(term: string) {
     return this.http
       .get(`https://api.spotify.com/v1/search?q=${term}&type=artist`)
       .map((response) => response.json())
       .toPromise();
}

你可以这样消费,

search() {
    this.searchService.search(this.searchField.value)
      .then((result) => {
    this.result = result.artists.items;
  })
  .catch((error) => console.error(error));
}

请参阅此处的 original 帖子。但是 TypeScript 不支持 native ES6 Promises,如果你想使用它,你可能需要插件。

此外,这里是 promises specification。

huntsbot.com提供全网独家一站式外包任务、远程工作、创意产品分享与订阅服务!

不过,这并没有解释 Promise 是如何解决这个问题的。

jQuery 和 fetch 方法也都返回 Promise。我建议修改你的答案。虽然 jQuery 的不太一样(然后在那里,但 catch 不是)。

解决方案9:

一个优秀的自由职业者,应该有对需求敏感和精准需求捕获的能力,而huntsbot.com提供了这个机会

这里的大多数答案都为您何时进行单个异步操作提供了有用的建议,但有时,当您需要对数组或其他类似列表的结构中的每个条目执行异步操作时,就会出现这种情况。这样做的诱惑是:

// WRONG
var results = [];
theArray.forEach(function(entry) {
    doSomethingAsync(entry, function(result) {
        results.push(result);
    });
});
console.log(results); // E.g., using them, returning them, etc.

例子:

// 错误 var theArray = [1, 2, 3];变种结果 = []; theArray.forEach(function(entry) { doSomethingAsync(entry, function(result) { results.push(result); }); }); console.log(“结果:”, 结果); // 例如,使用它们,返回它们等等 function doSomethingAsync(value, callback) { console.log("Starting async operation for " + value); setTimeout(function() { console.log(“完成异步操作” + value); callback(value * 2); }, Math.floor(Math.random() * 200)); } .as-console-wrapper { max-height: 100% !important; }

不起作用的原因是,当您尝试使用结果时,来自 doSomethingAsync 的回调尚未运行。

因此,如果您有一个数组(或某种列表)并且想要对每个条目执行异步操作,您有两个选择:并行(重叠)或串行(一个接一个地依次)执行操作。

平行

您可以启动所有这些并跟踪您期望的回调数量,然后在获得那么多回调时使用结果:

var results = [];
var expecting = theArray.length;
theArray.forEach(function(entry, index) {
    doSomethingAsync(entry, function(result) {
        results[index] = result;
        if (--expecting === 0) {
            // Done!
            console.log("Results:", results); // E.g., using the results
        }
    });
});

例子:

var theArray = [1, 2, 3];变种结果 = []; var 期待 = theArray.length; theArray.forEach(function(entry, index) { doSomethingAsync(entry, function(result) { results[index] = result; if (–expecting === 0) { // 完成!console.log(“Results:” , JSON.stringify(results)); // 例如,使用结果 } }); }); function doSomethingAsync(value, callback) { console.log("开始异步操作 " + value); setTimeout(function() { console.log(“完成异步操作” + value); callback(value * 2); }, Math.floor(Math.random() * 200)); } .as-console-wrapper { max-height: 100% !important; }

(我们可以取消 expecting 而只使用 results.length === theArray.length,但这让我们有可能在调用未完成时更改 theArray…)

请注意我们如何使用 forEach 中的 index 将结果保存在 results 中与其相关条目相同的位置,即使结果无序到达(因为异步调用不一定在它们开始的顺序)。

但是,如果您需要从函数中返回这些结果怎么办?正如其他答案所指出的那样,您不能;您必须让您的函数接受并调用回调(或返回 Promise)。这是一个回调版本:

function doSomethingWith(theArray, callback) {
    var results = [];
    var expecting = theArray.length;
    theArray.forEach(function(entry, index) {
        doSomethingAsync(entry, function(result) {
            results[index] = result;
            if (--expecting === 0) {
                // Done!
                callback(results);
            }
        });
    });
}
doSomethingWith(theArray, function(results) {
    console.log("Results:", results);
});

例子:

函数 doSomethingWith(theArray, callback) { var results = []; var 期待 = theArray.length; theArray.forEach(function(entry, index) { doSomethingAsync(entry, function(result) { results[index] = result; if (–expecting === 0) { // 完成!回调(results); } }) ; }); } doSomethingWith([1, 2, 3], function(results) { console.log(“Results:”, JSON.stringify(results)); }); function doSomethingAsync(value, callback) { console.log("开始异步操作 " + value); setTimeout(function() { console.log(“完成异步操作” + value); callback(value * 2); }, Math.floor(Math.random() * 200)); } .as-console-wrapper { max-height: 100% !important; }

或者这是一个返回 Promise 的版本:

function doSomethingWith(theArray) {
    return new Promise(function(resolve) {
        var results = [];
        var expecting = theArray.length;
        theArray.forEach(function(entry, index) {
            doSomethingAsync(entry, function(result) {
                results[index] = result;
                if (--expecting === 0) {
                    // Done!
                    resolve(results);
                }
            });
        });
    });
}
doSomethingWith(theArray).then(function(results) {
    console.log("Results:", results);
});

当然,如果 doSomethingAsync 向我们传递了错误,我们会在遇到错误时使用 reject 拒绝承诺。)

例子:

function doSomethingWith(theArray) { return new Promise(function(resolve) { var results = []; var expecting = theArray.length; theArray.forEach(function(entry, index) { doSomethingAsync(entry, function(result) { results[ index] = result; if (–expecting === 0) { // 完成!resolve(results); } }); }); }); } doSomethingWith([1, 2, 3]).then(function(results) { console.log(“Results:”, JSON.stringify(results)); }); function doSomethingAsync(value, callback) { console.log("开始异步操作 " + value); setTimeout(function() { console.log(“完成异步操作” + value); callback(value * 2); }, Math.floor(Math.random() * 200)); } .as-console-wrapper { max-height: 100% !important; }

(或者,您可以为 doSomethingAsync 创建一个返回承诺的包装器,然后执行以下操作…)

如果 doSomethingAsync 为您提供 Promise,您可以使用 Promise.all:

function doSomethingWith(theArray) {
    return Promise.all(theArray.map(function(entry) {
        return doSomethingAsync(entry);
    }));
}
doSomethingWith(theArray).then(function(results) {
    console.log("Results:", results);
});

如果您知道 doSomethingAsync 将忽略第二个和第三个参数,您可以直接将其传递给 map(map 使用三个参数调用它的回调,但大多数人大部分时间只使用第一个):

function doSomethingWith(theArray) {
    return Promise.all(theArray.map(doSomethingAsync));
}
doSomethingWith(theArray).then(function(results) {
    console.log("Results:", results);
});

例子:

函数 doSomethingWith(theArray) { return Promise.all(theArray.map(doSomethingAsync)); } doSomethingWith([1, 2, 3]).then(function(results) { console.log(“Results:”, JSON.stringify(results)); }); function doSomethingAsync(value) { console.log("开始异步操作 " + value); return new Promise(function(resolve) { setTimeout(function() { console.log(“完成异步操作” + value); resolve(value * 2); }, Math.floor(Math.random() * 200) ); }); } .as-console-wrapper { max-height: 100% !important; }

请注意,当您给它的所有承诺都被解决时,Promise.all 会使用您给它的所有承诺的结果数组来解决它的承诺,或者当您给它的 第一个 个承诺被拒绝时拒绝它的承诺。

系列

假设您不希望这些操作是并行的?如果你想一个接一个地运行它们,你需要等待每个操作完成后再开始下一个操作。这是执行此操作并使用结果调用回调的函数示例:

function doSomethingWith(theArray, callback) {
    var results = [];
    doOne(0);
    function doOne(index) {
        if (index < theArray.length) {
            doSomethingAsync(theArray[index], function(result) {
                results.push(result);
                doOne(index + 1);
            });
        } else {
            // Done!
            callback(results);
        }
    }
}
doSomethingWith(theArray, function(results) {
    console.log("Results:", results);
});

(由于我们是按顺序进行工作,我们可以只使用 results.push(result),因为我们知道我们不会得到乱序的结果。在上面我们可以使用 results[index] = result;,但在某些以下示例我们没有要使用的索引。)

例子:

函数 doSomethingWith(theArray, callback) { var results = [];做一个(0); function doOne(index) { if (index < theArray.length) { doSomethingAsync(theArray[index], function(result) { results.push(result); doOne(index + 1); }); } else { // 完成!回调(结果); } } } doSomethingWith([1, 2, 3], function(results) { console.log(“Results:”, JSON.stringify(results)); }); function doSomethingAsync(value, callback) { console.log("开始异步操作 " + value); setTimeout(function() { console.log(“完成异步操作” + value); callback(value * 2); }, Math.floor(Math.random() * 200)); } .as-console-wrapper { max-height: 100% !important; }

(或者,再次为 doSomethingAsync 构建一个包装器,为您提供承诺并执行以下操作…)

如果 doSomethingAsync 给您一个 Promise,如果您可以使用 ES2017+ 语法(可能使用像 Babel 这样的转译器),您可以将 async function 与 for-of 和 await 一起使用:

async function doSomethingWith(theArray) {
    const results = [];
    for (const entry of theArray) {
        results.push(await doSomethingAsync(entry));
    }
    return results;
}
doSomethingWith(theArray).then(results => {
    console.log("Results:", results);
});

例子:

异步函数 doSomethingWith(theArray) { const 结果 = []; for (const entry of theArray) { results.push(await doSomethingAsync(entry)); } 返回结果; } doSomethingWith([1, 2, 3]).then(function(results) { console.log(“Results:”, JSON.stringify(results)); }); function doSomethingAsync(value) { console.log("开始异步操作 " + value); return new Promise(function(resolve) { setTimeout(function() { console.log(“完成异步操作” + value); resolve(value * 2); }, Math.floor(Math.random() * 200) ); }); } .as-console-wrapper { max-height: 100% !important; }

如果你还不能使用 ES2017+ 语法,你可以使用 “Promise reduce” pattern 的变体(这比通常的 Promise reduce 更复杂,因为我们不会将结果从一个传递到下一个,而是收集他们的结果在一个数组中):

function doSomethingWith(theArray) {
    return theArray.reduce(function(p, entry) {
        return p.then(function(results) {
            return doSomethingAsync(entry).then(function(result) {
                results.push(result);
                return results;
            });
        });
    }, Promise.resolve([]));
}
doSomethingWith(theArray).then(function(results) {
    console.log("Results:", results);
});

例子:

function doSomethingWith(theArray) { return theArray.reduce(function(p, entry) { return p.then(function(results) { return doSomethingAsync(entry).then(function(result) { results.push(result); 返回结果; }); }); }, Promise.resolve([])); } doSomethingWith([1, 2, 3]).then(function(results) { console.log(“Results:”, JSON.stringify(results)); }); function doSomethingAsync(value) { console.log("开始异步操作 " + value); return new Promise(function(resolve) { setTimeout(function() { console.log(“完成异步操作” + value); resolve(value * 2); }, Math.floor(Math.random() * 200) ); }); } .as-console-wrapper { max-height: 100% !important; }

…使用 ES2015+ arrow functions 不太麻烦:

function doSomethingWith(theArray) {
    return theArray.reduce((p, entry) => p.then(results => doSomethingAsync(entry).then(result => {
        results.push(result);
        return results;
    })), Promise.resolve([]));
}
doSomethingWith(theArray).then(results => {
    console.log("Results:", results);
});

例子:

function doSomethingWith(theArray) { return theArray.reduce((p, entry) => p.then(results => doSomethingAsync(entry).then(result => { results.push(result); return results; })), Promise.resolve([])); } doSomethingWith([1, 2, 3]).then(function(results) { console.log(“Results:”, JSON.stringify(results)); }); function doSomethingAsync(value) { console.log("开始异步操作 " + value); return new Promise(function(resolve) { setTimeout(function() { console.log(“完成异步操作” + value); resolve(value * 2); }, Math.floor(Math.random() * 200) ); }); } .as-console-wrapper { max-height: 100% !important; }

您能解释一下代码的 if (--expecting === 0) 部分是如何工作的吗?您的解决方案的回调版本对我来说非常有用,我只是不明白您如何使用该语句检查已完成的响应数量。感谢这只是我缺乏知识。有没有另一种方法可以写支票?

@Sarah:expecting 以 array.length 的值开始,即我们要发出多少请求。我们知道在所有这些请求开始之前不会调用回调。在回调中,if (--expecting === 0) 执行以下操作: 1. 递减 expecting(我们已收到响应,因此我们预计会少一个响应)并且如果值 after 减量为 0(我们不再期待任何回应),我们完成了!

@Henke - 我认为这确实是个人喜好,虽然通常我更喜欢记录原始数据并让控制台处理它,但在这种特定情况下,我认为你对更改是正确的。谢谢! :-)

为了我自己(和其他人?)的方便,添加指向相关答案的链接:How to make many asynchronous calls and wait for them all。

解决方案10:

打造属于自己的副业,开启自由职业之旅,从huntsbot.com开始!

看看这个例子:

var app = angular.module('plunker', []);

app.controller('MainCtrl', function($scope,$http) {

    var getJoke = function(){
        return $http.get('http://api.icndb.com/jokes/random').then(function(res){
            return res.data.value;  
        });
    }

    getJoke().then(function(res) {
        console.log(res.joke);
    });
});

如您所见,getJoke 返回一个 已解决的 promise(返回 res.data.value 时已解决)。所以你等到 $http.get 请求完成,然后执行 console.log(res.joke) (作为一个正常的异步流程)。

这是 plnkr:

http://embed.plnkr.co/XlNR7HpCaIhJxskMJfSg/

ES6 方式(异步 - 等待)

(function(){
  async function getJoke(){
    let response = await fetch('http://api.icndb.com/jokes/random');
    let data = await response.json();
    return data.value;
  }

  getJoke().then((joke) => {
    console.log(joke);
  });
})();

解决方案11:

huntsbot.com精选全球7大洲远程工作机会,涵盖各领域,帮助想要远程工作的数字游民们能更精准、更高效的找到对方。

这是在许多新的 JavaScript 框架中使用的双向数据绑定或存储概念对您非常有用的地方之一…

因此,如果您使用 Angular、React 或任何其他执行双向数据绑定或存储概念的框架,则此问题已为您解决,简单来说,您的结果首先是 undefined阶段,因此您在收到数据之前已经获得了 result = undefined,然后一旦您获得结果,它将被更新并分配给您的 Ajax 调用响应的新值…

但是,例如,正如您在这个问题中所问的那样,您如何在纯 JavaScript 或 jQuery 中做到这一点?

你可以使用回调、promise 和最近 observable 来为你处理它。例如,在 Promise 中,我们有一些像 success() 或 then() 这样的函数,它们将在您的数据准备好时执行。与可观察对象上的回调或 subscribe 函数相同。

例如,在您使用 jQuery 的情况下,您可以执行以下操作:

$(document).ready(function(){
    function foo() {
        $.ajax({url: "api/data", success: function(data){
            fooDone(data); // After we have data, we pass it to fooDone
        }});
    };

    function fooDone(data) {
        console.log(data); // fooDone has the data and console.log it
    };

    foo(); // The call happens here
});

有关更多信息,请研究 promises 和 observables,它们是执行此异步操作的新方法。

这在全局范围内很好,但在某些模块上下文中,您可能希望确保回调的正确上下文,例如 $.ajax({url: "api/data", success: fooDone.bind(this)});

这实际上是不正确的,因为 React 是单向数据绑定

@MatthewBrent 你没有错,但也不对,React 道具是对象,如果更改,它们会在整个应用程序中更改,但这不是 React 开发人员推荐使用它的方式...

原文链接:https://www.huntsbot.com/qa/aL2l/how-do-i-return-the-response-from-an-asynchronous-call?lang=zh_CN

huntsbot.com高效搞钱,一站式跟进超10+任务平台外包需求