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

我有一个函数foo,该函数发出Ajax请求。如何从foo返回响应?

我尝试从success回调中返回值,并将响应分配给函数内部的局部变量并返回该局部变量,但这些方法均未真正返回响应。

function foo() {
    var result;

    $.ajax({
        url: '...',
        success: function(response) {
            result = response;
            // return response; // <- I tried that one as well
        }
    });

    return result;
}

var result = foo(); // It always ends up being `undefined`.
标签:javascript,jquery,ajax,asynchronous

Q: How do I return the response from an asynchronous call?

I have a function foo which makes an Ajax request. How can I return the response from foo?

I tried returning the value from the success callback as well as assigning the response to a local variable inside the function and returning that one, but none of those ways actually return the response.

function foo() {
    var result;

    $.ajax({
        url: '...',
        success: function(response) {
            result = response;
            // return response; // <- I tried that one as well
        }
    });

    return result;
}

var result = foo(); // It always ends up being `undefined`.

回答1:

→有关使用不同示例的异步行为的更一般说明,请参见 为什么我的变量未更改在函数内部修改它之后? -异步代码参考

→如果您已经了解问题,请跳到下面的可能解决方案。

问题

Ajax 中的 A 代表 异步 。这意味着发送请求(或更确切地说接收响应)已从正常执行流程中删除。在您的示例中,$.ajax立即返回,下一条语句returnresult;在执行作为success回调传递的函数之前执行甚至被称为。

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

同步

想象一下,您给朋友打了电话,并请他为您找东西。尽管可能要花一些时间,但您还是要等电话并凝视太空,直到您的朋友给您所需的答案。

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

function findItem() {
    var item;
    while(item_not_found) {
        // search
    }
    return item;
}

var item = findItem();

// Do something with item
doSomethingElse();

即使findItem可能要花很长时间才能执行,但varitem=findItem();之后的任何代码都必须 wait 直到该函数返回结果。

异步

出于相同的原因,您再次致电您的朋友。但是这次您告诉他您很着急,他应该在您的手机上给您回电。您挂断电话,离开房子,然后按计划做。一旦您的朋友给您回电,您就可以处理他提供给您的信息。

这就是您执行Ajax请求时发生的事情。

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

不是等待响应,而是立即继续执行,并执行Ajax调用后的语句。为了最终获得响应,您提供了一个在收到响应后立即调用的函数,即 callback (注意吗? callback ?)。在调用之后执行的所有语句都将在调用回调之前执行。


解决方案

拥抱JavaScript的异步特性!虽然某些异步操作提供了同步的对应功能(" Ajax"也是如此),但通常不鼓励使用它们,尤其是在浏览器上下文中。

为什么问得不好?

JavaScript在浏览器的UI线程中运行,任何长时间运行的进程都将锁定UI,从而使其无响应。此外,JavaScript的执行时间有上限,浏览器会询问用户是否继续执行。

所有这些都是非常糟糕的用户体验。用户将无法判断一切是否正常。此外,对于连接速度较慢的用户,效果会更糟。

在下面,我们将研究三个互为基础的不同解决方案:

  • 具有async/await 的承诺(ES2017 +,如果使用转译器或再生器,则在较旧的浏览器中可用)
  • 回调(在节点中受欢迎)
  • 具有then() 的承诺(ES2015 +,如果您使用多个promise库之一,则在较旧的浏览器中可用)

所有三个功能均在当前浏览器以及节点7+中可用。


ES2017 +:带有 async/await的承诺

2017年发布的ECMAScript版本引入了对异步功能的语法级支持。借助asyncawait,您可以以"同步样式"编写异步代码。该代码仍然是异步的,但更易于阅读/理解。

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

重要:您只能在async函数内使用await。目前,尚不支持顶级await,因此您可能必须进行异步IIFE(立即调用函数表达式)以启动async上下文。

您可以阅读有关 async await 在MDN上。

以下是基于上述延迟的示例:

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

当前浏览器节点版本支持async/await。您还可以在 regenerator (或使用regenerator的工具)的帮助下,通过将代码转换为ES5来支持较旧的环境。 ,例如 Babel )。


让函数接受回调

回调只是传递给另一个函数的一个函数。其他函数可以随时调用传递的函数。在异步过程的上下文中,只要异步过程完成,就会调用回调。通常,结果将传递给回调。

在问题示例中,您可以使foo接受回调并将其用作成功回调。所以这个

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

回调将引用调用时传递给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)的功能,但已经具有良好的浏览器支持。还有许多实现标准Promises API的库,并提供其他方法来简化异步函数的使用和组合(例如 bluebird )。

承诺是未来值的容器。当promise接收到该值(已被已解决)或被取消(被被拒绝)时,它会通知其所有要访问此值的"监听器"。 / p>

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

这是一个使用诺言的简单示例:

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

应用于我们的Ajax调用,我们可以使用如下承诺:

function 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("/echo/json")
  .then(function(result) {
    // Code depending on result
  })
  .catch(function() {
    // An error occurred
  });

描述promise所提供的所有优点超出了此答案的范围,但是如果您编写新代码,则应认真考虑它们。它们为您的代码提供了很好的抽象和分离。

有关承诺的更多信息: HTML5摇滚-JavaScript承诺

侧面说明:jQuery的延迟对象

延迟对象是jQuery的promise自定义实现(在Promise API标准化之前)。它们的行为几乎像承诺一样,但暴露了稍微不同的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 对象,请传递< code> false 作为 的第三个参数.open

jQuery

如果您使用 jQuery ,则可以设置async选项为false。请注意,自jQuery 1.8起,该选项已被弃用。然后,您仍然可以使用success回调或访问 responseText属性。 jqXHR" rel="noreferrer"> jqXHR对象:

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

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

注意!无法同步 JSONP 请求。 JSONP本质上总是异步的(还有一个甚至不考虑此选项的原因)。

A1:

→ For a more general explanation of async behaviour with different examples, please see Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference

→ If you already understand the problem, skip to the possible solutions below.

The problem

The A in Ajax stands for asynchronous . That means sending the request (or rather receiving the response) is taken out of the normal execution flow. In your example, $.ajax returns immediately and the next statement, return result;, is executed before the function you passed as success callback was even called.

Here is an analogy which hopefully makes the difference between synchronous and asynchronous flow clearer:

Synchronous

Imagine you make a phone call to a friend and ask him to look something up for you. Although it might take a while, you wait on the phone and stare into space, until your friend gives you the answer that you needed.

The same is happening when you make a function call containing "normal" code:

function findItem() {
    var item;
    while(item_not_found) {
        // search
    }
    return item;
}

var item = findItem();

// Do something with item
doSomethingElse();

Even though findItem might take a long time to execute, any code coming after var item = findItem(); has to wait until the function returns the result.

Asynchronous

You call your friend again for the same reason. But this time you tell him that you are in a hurry and he should call you back on your mobile phone. You hang up, leave the house and do whatever you planned to do. Once your friend calls you back, you are dealing with the information he gave to you.

That's exactly what's happening when you do an Ajax request.

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

Instead of waiting for the response, the execution continues immediately and the statement after the Ajax call is executed. To get the response eventually, you provide a function to be called once the response was received, a callback (notice something? call back ?). Any statement coming after that call is executed before the callback is called.


Solution(s)

Embrace the asynchronous nature of JavaScript! While certain asynchronous operations provide synchronous counterparts (so does "Ajax"), it's generally discouraged to use them, especially in a browser context.

Why is it bad do you ask?

JavaScript runs in the UI thread of the browser and any long-running process will lock the UI, making it unresponsive. Additionally, there is an upper limit on the execution time for JavaScript and the browser will ask the user whether to continue the execution or not.

All of this is really bad user experience. The user won't be able to tell whether everything is working fine or not. Furthermore, the effect will be worse for users with a slow connection.

In the following we will look at three different solutions that are all building on top of each other:

  • Promises with async/await (ES2017+, available in older browsers if you use a transpiler or regenerator)
  • Callbacks (popular in node)
  • Promises with then() (ES2015+, available in older browsers if you use one of the many promise libraries)

All three are available in current browsers, and node 7+.


ES2017+: Promises with async/await

The ECMAScript version released in 2017 introduced syntax-level support for asynchronous functions. With the help of async and await, you can write asynchronous in a "synchronous style". The code is still asynchronous, but it's easier to read/understand.

async/await builds on top of promises: an async function always returns a promise. await "unwraps" a promise and either result in the value the promise was resolved with or throws an error if the promise was rejected.

Important: You can only use await inside an async function. Right now, top-level await isn't yet supported, so you might have to make an async IIFE (Immediately Invoked Function Expression) to start an async context.

You can read more about async and await on MDN.

Here is an example that builds on top of delay above:

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

Current browser and node versions support async/await. You can also support older environments by transforming your code to ES5 with the help of regenerator (or tools that use regenerator, such as Babel).


Let functions accept callbacks

A callback is simply a function passed to another function. That other function can call the function passed whenever it is ready. In the context of an asynchronous process, the callback will be called whenever the asynchronous process is done. Usually, the result is passed to the callback.

In the example of the question, you can make foo accept a callback and use it as success callback. So this

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

becomes

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

Here we defined the function "inline" but you can pass any function reference:

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

foo(myCallback);

foo itself is defined as follows:

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

callback will refer to the function we pass to foo when we call it and we simply pass it on to success. I.e. once the Ajax request is successful, $.ajax will call callback and pass the response to the callback (which can be referred to with result, since this is how we defined the callback).

You can also process the response before passing it to the callback:

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

It's easier to write code using callbacks than it may seem. After all, JavaScript in the browser is heavily event driven (DOM events). Receiving the Ajax response is nothing else but an event.
Difficulties could arise when you have to work with third-party code, but most problems can be solved by just thinking through the application flow.


ES2015+: Promises with then()

The Promise API is a new feature of ECMAScript 6 (ES2015), but it has good browser support already. There are also many libraries which implement the standard Promises API and provide additional methods to ease the use and composition of asynchronous functions (e.g. bluebird).

Promises are containers for future values. When the promise receives the value (it is resolved) or when it is canceled (rejected), it notifies all of its "listeners" who want to access this value.

The advantage over plain callbacks is that they allow you to decouple your code and they are easier to compose.

Here is a simple example of using 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);
  });
}

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

Applied to our Ajax call we could use promises like this:

function 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("/echo/json")
  .then(function(result) {
    // Code depending on result
  })
  .catch(function() {
    // An error occurred
  });

Describing all the advantages that promise offer is beyond the scope of this answer, but if you write new code, you should seriously consider them. They provide a great abstraction and separation of your code.

More information about promises: HTML5 rocks - JavaScript Promises

Side note: jQuery's deferred objects

Deferred objects are jQuery's custom implementation of promises (before the Promise API was standardized). They behave almost like promises but expose a slightly different API.

Every Ajax method of jQuery already returns a "deferred object" (actually a promise of a deferred object) which you can just return from your function:

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

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

Side note: Promise gotchas

Keep in mind that promises and deferred objects are just containers for a future value, they are not the value itself. For example, suppose you had the following:

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
}

This code misunderstands the above asynchrony issues. Specifically, $.ajax() doesn't freeze the code while it checks the '/password' page on your server - it sends a request to the server and while it waits, it immediately returns a jQuery Ajax Deferred object, not the response from the server. That means the if statement is going to always get this Deferred object, treat it as true, and proceed as though the user is logged in. Not good.

But the fix is easy:

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

Not recommended: Synchronous "Ajax" calls

As I mentioned, some(!) asynchronous operations have synchronous counterparts. I don't advocate their use, but for completeness' sake, here is how you would perform a synchronous call:

Without jQuery

If you directly use a XMLHTTPRequest object, pass false as third argument to .open.

jQuery

If you use jQuery, you can set the async option to false. Note that this option is deprecated since jQuery 1.8. You can then either still use a success callback or access the responseText property of the jqXHR object:

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

If you use any other jQuery Ajax method, such as $.get, $.getJSON, etc., you have to change it to $.ajax (since you can only pass configuration parameters to $.ajax).

Heads up! It is not possible to make a synchronous JSONP request. JSONP by its very nature is always asynchronous (one more reason to not even consider this option).

回答2:

如果您在代码中使用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很好地为使用jQuery for AJAX的人编写了答案,我决定为那些没有使用jQuery的人提供替代方案。

( =" 注意,对于使用新的fetch API的用户,Angular或Promise我已经添加了另一个答案下面)


您面临的情况

这是另一个答案中"问题的解释"的简短摘要,如果不确定阅读此内容后,请阅读该内容。

AJAX中的 A 代表异步。这意味着发送请求(或更确切地说接收响应)已从正常执行流程中删除。在您的示例中, .send 立即返回,并在调用您作为success回调传递的函数之前执行下一条语句returnresult;

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

这是一个简单的比喻

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

(小提琴)

返回的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调用,或者在这种情况下是超时)。 / p>

用法是:

getFive(onComplete);

应该在屏幕上提示" 5"。 (小提琴)

可能的解决方案

基本上有两种解决方法:

  1. 使AJAX调用同步(将其称为SJAX)。
  2. 重组代码以使其与回调一起正常工作。

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

对于同步AJAX,不要这样做!。Felix的回答提出了一些令人信服的论点,说明为什么这是一个坏主意。总结起来,它将冻结用户的浏览器,直到服务器返回响应并创建非常糟糕的用户体验。这是MDN关于原因的另一个简短摘要:

XMLHttpRequest支持同步和异步通信。但是,一般而言,出于性能方面的考虑,异步请求应优先于同步请求。

简而言之,同步请求会阻止代码的执行……这可能会导致严重的问题……

如果必须做到这一点,则可以传递一个标志:此处是方法:

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中的指南

A2:

If you're not using jQuery in your code, this answer is for you

Your code should be something along the lines of this:

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 writing an answer for people using jQuery for AJAX, I've decided to provide an alternative for people who aren't.

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


What you're facing

This is a short summary of "Explanation of the problem" from the other answer, if you're not sure after reading this, read that.

The A in AJAX stands for asynchronous. That means sending the request (or rather receiving the response) is taken out of the normal execution flow. In your example, .send returns immediately and the next statement, return result;, is executed before the function you passed as success callback was even called.

This means when you're returning, the listener you've defined did not execute yet, which means the value you're returning has not been defined.

Here is a simple analogy

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

(Fiddle)

The value of a returned is undefined since the a=5 part has not executed yet. AJAX acts like this, you're returning the value before the server got the chance to tell your browser what that value is.

One possible solution to this problem is to code re-actively , telling your program what to do when the calculation completed.

function onComplete(a){ // When the code completes, do this
    alert(a);
}

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

This is called CPS. Basically, we're passing getFive an action to perform when it completes, we're telling our code how to react when an event completes (like our AJAX call, or in this case the timeout).

Usage would be:

getFive(onComplete);

Which should alert "5" to the screen. (Fiddle).

Possible solutions

There are basically two ways how to solve this:

  1. Make the AJAX call synchronous (lets call it SJAX).
  2. Restructure your code to work properly with callbacks.

1. Synchronous AJAX - Don't do it!!

As for synchronous AJAX, don't do it! Felix's answer raises some compelling arguments about why it's a bad idea. To sum it up, it'll freeze the user's browser until the server returns the response and create a very bad user experience. Here is another short summary taken from MDN on why:

XMLHttpRequest supports both synchronous and asynchronous communications. In general, however, asynchronous requests should be preferred to synchronous requests for performance reasons.

In short, synchronous requests block the execution of code... ...this can cause serious issues...

If you have to do it, you can pass a flag: 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. Restructure code

Let your function accept a callback. In the example code foo can be made to accept a callback. We'll be telling our code how to react when foo completes.

So:

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

Becomes:

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

Here we passed an anonymous function, but we could just as easily pass a reference to an existing function, making it look like:

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

For more details on how this sort of callback design is done, check Felix's answer.

Now, let's define foo itself to act accordingly

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)

We have now made our foo function accept an action to run when the AJAX completes successfully, we can extend this further by checking if the response status is not 200 and acting accordingly (create a fail handler and such). Effectively solving our issue.

If you're still having a hard time understanding this read the AJAX getting started guide at MDN.

回答3:

XMLHttpRequest 2 (首先阅读< a href=" https://stackoverflow.com/a/16825593/19068"> 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()
}

如您所见:

  1. 比列出的所有其他功能短。
  2. 直接设置回调(因此不会造成不必要的关闭)。
  3. 它使用新的onload(因此您不必检查readystate和&状态)
  4. 我不记得还有其他一些情况使XMLHttpRequest 1令人讨厌。

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

最简单的

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高级功能

它支持所有*现代浏览器。我可以确认,因为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)
}

再次...这是一个非常短的函数,但是它确实可以获取并发布。

用法示例:

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不允许的方法.

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

不允许跨域自动引发错误。

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

只有this.type设置为错误。

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

因此:如果您能够正确复制和粘贴网址,则无需进行错误检查。 ;)

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响应...(我添加了一种简单的方法。)

A3:

XMLHttpRequest 2 (first of all read the answers from Benjamin Gruenbaum & Felix Kling)

If you don't use jQuery and want a nice short XMLHttpRequest 2 which works on the modern browsers and also on the mobile browsers I suggest to use it this way:

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

As you can see:

  1. It's shorter than all other functions Listed.
  2. The callback is set directly (so no extra unnecessary closures).
  3. It uses the new onload (so you don't have to check for readystate && status)
  4. There are some other situations which I don't remember that make the XMLHttpRequest 1 annoying.

There are two ways to get the response of this Ajax call (three using the XMLHttpRequest var name):

The simplest:

this.response

Or if for some reason you bind() the callback to a class:

e.target.response

Example:

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

Or (the above one is better anonymous functions are always a problem):

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

Nothing easier.

Now some people will probably say that it's better to use onreadystatechange or the even the XMLHttpRequest variable name. That's wrong.

Check out XMLHttpRequest advanced features

It supported all *modern browsers. And I can confirm as I'm using this approach since XMLHttpRequest 2 exists. I never had any type of problem on all browsers I use.

onreadystatechange is only useful if you want to get the headers on state 2.

Using the XMLHttpRequest variable name is another big error as you need to execute the callback inside the onload/oreadystatechange closures else you lost it.


Now if you want something more complex using post and FormData you can easily extend this function:

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

Again ... it's a very short function, but it does get & post.

Examples of usage:

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

Or pass a full form element (document.getElementsByTagName('form')[0]):

var fd = new FormData(form);
x(url, callback, 'post', fd);

Or set some custom values:

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

As you can see I didn't implement sync... it's a bad thing.

Having said that ... why don't do it the easy way?


As mentioned in the comment the use of error && synchronous does completely break the point of the answer. Which is a nice short way to use Ajax in the proper way?

Error handler

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

In the above script, you have an error handler which is statically defined so it does not compromise the function. The error handler can be used for other functions too.

But to really get out an error the only way is to write a wrong URL in which case every browser throws an error.

Error handlers are maybe useful if you set custom headers, set the responseType to blob array buffer or whatever...

Even if you pass 'POSTAPAPAP' as the method it won't throw an error.

Even if you pass 'fdggdgilfdghfldj' as formdata it won't throw an error.

In the first case the error is inside the displayAjax() under this.statusText as Method not Allowed.

In the second case, it simply works. You have to check at the server side if you passed the right post data.

cross-domain not allowed throws error automatically.

In the error response, there are no error codes.

There is only the this.type which is set to error.

Why add an error handler if you totally have no control over errors? Most of the errors are returned inside this in the callback function displayAjax().

So: No need for error checks if you're able to copy and paste the URL properly. ;)

PS: As the first test I wrote x('x', displayAjax)..., and it totally got a response...??? So I checked the folder where the HTML is located, and there was a file called 'x.xml'. So even if you forget the extension of your file XMLHttpRequest 2 WILL FIND IT. I LOL'd


Read a file synchronous

Don't do that.

If you want to block the browser for a while load a nice big .txt file synchronous.

function omg(a, c){ // URL
  c = new XMLHttpRequest;
  c.open('GET', a, true);
  c.send();
  return c; // Or c.response
}

Now you can do

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

There is no other way to do this in a non-asynchronous way. (Yeah, with setTimeout loop... but seriously?)

Another point is... if you work with APIs or just your own list's files or whatever you always use different functions for each request...

Only if you have a page where you load always the same XML/JSON or whatever you need only one function. In that case, modify a little the Ajax function and replace b with your special function.


The functions above are for basic use.

If you want to EXTEND the function...

Yes, you can.

I'm using a lot of APIs and one of the first functions I integrate into every HTML page is the first Ajax function in this answer, with GET only...

But you can do a lot of stuff with XMLHttpRequest 2:

I made a download manager (using ranges on both sides with resume, filereader, filesystem), various image resizers converters using canvas, populate web SQL databases with base64images and much more... But in these cases you should create a function only for that purpose... sometimes you need a blob, array buffers, you can set headers, override mimetype and there is a lot more...

But the question here is how to return an Ajax response... (I added an easy way.)

回答4:

如果您使用的是诺言,那么这个答案就是给您的。

这意味着AngularJS,jQuery(具有延迟),本机XHR的替换(获取),EmberJS,BackboneJS的保存或任何返回诺言的节点库。

您的代码应与此类似:

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很好地为使用jQuery和AJAX回调的人们编写了答案。我对本地XHR有一个答案。这个答案是针对promise在前端或后端的一般用法。


核心问题

浏览器和具有NodeJS / io.js的服务器上的JavaScript并发模型是异步反应型

无论何时调用返回承诺的方法,then处理程序都会始终异步执行-即,在其下面的代码之后不在.then处理程序中。

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

以下是该问题的简单类比:

    function getFive(){
        var data;
        setTimeout(function(){ // set a timer for one second in the future
           data = 5; // after a second, do this
        }, 1000);
        return data;
    }
    document.body.innerHTML = getFive(); // `undefined` here and not 5

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

由于尚未执行该操作(AJAX,服务器调用,IO,计时器),因此您将在请求有机会告诉代码该值之前返回该值。

该问题的一种可能解决方案是 active 进行编码,告诉您的程序计算完成后该怎么做。承诺本质上是时间(时间敏感)的,因此可以积极地实现这一目标。

快速回顾承诺

承诺是一段时间内的。承诺有状态,它们以没有价值的待处理状态开始,可以解决:

  • 完成表示计算成功完成。
  • 已拒绝,表示计算失败。

承诺只能更改状态一次,此后它将永远永远保持相同状态。您可以将then处理程序附加到Promise,以提取其值并处理错误。 then处理程序允许呼叫链接。承诺是通过使用返回它们的API来创建的。例如,更现代的AJAX替换fetch或jQuery的$.get返回承诺。

当我们在一个承诺上调用.then并从中返回 时,我们得到了一个处理后价值的承诺。如果我们再次兑现诺言,我们将得到令人惊奇的东西,但让我们坚持不懈。

有诺言

让我们看看如何用承诺解决上述问题。首先,让我们通过使用 Promise构造函数,用于创建延迟函数:

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

现在,在将setTimeout转换为使用Promise之后,我们可以使用then进行计数:

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

function getFive(){
  // we're RETURNING the promise, remember, a promise is a wrapper over our value
  return delay(100).then(function(){ // when the promise is ready
      return 5; // return the value 5, promises are all about return values
  })
}
// we _have_ to wrap it like this in the call site, we can't access the plain value
getFive().then(function(five){ 
   document.body.innerHTML = five;
});

基本上,与其返回由于并发模型而无法执行的 value ,我们返回的是 wrapper 作为我们可以的值>先用然后解包。就像一个可以用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引入了生成器这些函数可以在中间返回,然后恢复它们所处的位置。这通常对序列很有用,例如:

function* foo(){ // notice the star, this is ES6 so new browsers/node/io only
    yield 1;
    yield 2;
    while(true) yield 3;
}

是一个函数,该函数在可以迭代的1,2,3,3,3,3,....序列上返回 iterator 。尽管这本身很有趣,并且为很多可能性打开了空间,但是有一个特别有趣的情况。

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

这个有点棘手但非常强大的技巧使我们能够以同步方式编写异步代码。有几个"运行器"可以为您执行此操作,编写一小段代码即可,但超出了此答案的范围。我将在这里使用Bluebird的Promise.coroutine,但是还有其他包装器,例如co<​​/code>或Q.async

var foo = coroutine(function*(){
    var data = yield fetch("/echo/json"); // notice the yield
    // 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
   // server call done here, 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 done
});
main();

ES2016(ES7)

在ES7中,这是进一步标准化的,目前有几个建议,但是在所有建议中,您都可以await promise。通过添加asyncawait关键字,这只是上述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
}

它仍然返回一个承诺:)

A4:

If you're using promises, this answer is for you.

This means AngularJS, jQuery (with deferred), native XHR's replacement (fetch), EmberJS, BackboneJS's save or any node library that returns promises.

Your code should be something along the lines of this:

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 writing an answer for people using jQuery with callbacks for AJAX. I have an answer for native XHR. This answer is for generic usage of promises either on the frontend or backend.


The core issue

The JavaScript concurrency model in the browser and on the server with NodeJS/io.js is asynchronous and reactive.

Whenever you call a method that returns a promise, the then handlers are always executed asynchronously - that is, after the code below them that is not in a .then handler.

This means when you're returning data the then handler you've defined did not execute yet. This in turn means that the value you're returning has not been set to the correct value in time.

Here is a simple analogy for the issue:

    function getFive(){
        var data;
        setTimeout(function(){ // set a timer for one second in the future
           data = 5; // after a second, do this
        }, 1000);
        return data;
    }
    document.body.innerHTML = getFive(); // `undefined` here and not 5

The value of data is undefined since the data = 5 part has not executed yet. It will likely execute in a second but by that time it is irrelevant to the returned value.

Since the operation did not happen yet (AJAX, server call, IO, timer) you're returning the value before the request got the chance to tell your code what that value is.

One possible solution to this problem is to code re-actively , telling your program what to do when the calculation completed. Promises actively enable this by being temporal (time-sensitive) in nature.

Quick recap on promises

A Promise is a value over time. Promises have state, they start as pending with no value and can settle to:

  • fulfilled meaning that the computation completed successfully.
  • rejected meaning that the computation failed.

A promise can only change states once after which it will always stay at the same state forever. You can attach then handlers to promises to extract their value and handle errors. then handlers allow chaining of calls. Promises are created by using APIs that return them. For example, the more modern AJAX replacement fetch or jQuery's $.get return promises.

When we call .then on a promise and return something from it - we get a promise for the processed value. If we return another promise we'll get amazing things, but let's hold our horses.

With promises

Let's see how we can solve the above issue with promises. First, let's demonstrate our understanding of promise states from above by using the Promise constructor for creating a delay function:

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

Now, after we converted setTimeout to use promises, we can use then to make it count:

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

function getFive(){
  // we're RETURNING the promise, remember, a promise is a wrapper over our value
  return delay(100).then(function(){ // when the promise is ready
      return 5; // return the value 5, promises are all about return values
  })
}
// we _have_ to wrap it like this in the call site, we can't access the plain value
getFive().then(function(five){ 
   document.body.innerHTML = five;
});

Basically, instead of returning a value which we can't do because of the concurrency model - we're returning a wrapper for a value that we can unwrap with then. It's like a box you can open with then.

Applying this

This stands the same for your original API call, you can:

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

So this works just as well. We've learned we can't return values from already asynchronous calls but we can use promises and chain them to perform processing. We now know how to return the response from an asynchronous call.

ES2015 (ES6)

ES6 introduces generators which are functions that can return in the middle and then resume the point they were at. This is typically useful for sequences, for example:

function* foo(){ // notice the star, this is ES6 so new browsers/node/io only
    yield 1;
    yield 2;
    while(true) yield 3;
}

Is a function that returns an iterator over the sequence 1,2,3,3,3,3,.... which can be iterated. While this is interesting on its own and opens room for a lot of possibility there is one particular interesting case.

If the sequence we're producing is a sequence of actions rather than numbers - we can pause the function whenever an action is yielded and wait for it before we resume the function. So instead of a sequence of numbers, we need a sequence of future values - that is: promises.

This somewhat tricky but very powerful trick lets us write asynchronous code in a synchronous manner. There are several "runners" that do this for you, writing one is a short few lines of code but is beyond the scope of this answer. I'll be using Bluebird's Promise.coroutine here, but there are other wrappers like co or Q.async.

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

This method returns a promise itself, which we can consume from other coroutines. For example:

var main = coroutine(function*(){
   var bar = yield foo(); // wait our earlier coroutine, it returns a promise
   // server call done here, 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 done
});
main();

ES2016 (ES7)

In ES7, this is further standardized, there are several proposals right now but in all of them you can await promise. This is just "sugar" (nicer syntax) for the ES6 proposal above by adding the async and await keywords. Making the above example:

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
}

It still returns a promise just the same :)

回答5:

您使用的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);
    }
});

在提交处理程序中返回任何内容都不会执行任何操作。相反,您必须交出数据,或者直接在成功函数中执行所需的操作。

A5:

You are using Ajax incorrectly. The idea is not to have it return anything, but instead hand off the data to something called a callback function, which handles the data.

That is:

function handleData( responseData ) {

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

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

Returning anything in the submit handler will not do anything. You must instead either hand off the data, or do what you want with it directly inside the success function.

回答6:

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

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

A6:

The simplest solution is create a JavaScript function and call it for the Ajax success callback.

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

回答7:

我将用恐怖的手绘漫画回答。第二张图片是代码示例中result未定义的原因。

在此处输入图片描述

A7:

I will answer with a horrible-looking, hand-drawn comic. The second image is the reason why result is undefined in your code example.

enter image description here

回答8:

Angular1

对于正在使用 AngularJS 的用户,可以使用Promises处理这种情况.代码>。

此处

Promise可用于嵌套异步功能,并允许将多个功能链接在一起。

您还可以在此处找到很好的解释。

>

在下面提到的文档中找到示例。

  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.

Angular2及更高版本

Angular2中,请看以下示例,但其推荐,将ObservablesAngular2一起使用。

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

请参见此处的原始帖子。但是Typescript不支持 native es6 Promises ,如果要使用它,可能需要插件为此。

此外,这里的承诺规范在这里定义。

A8:

Angular1

For people who are using AngularJS, can handle this situation using Promises.

Here it says,

Promises can be used to unnest asynchronous functions and allows one to chain multiple functions together.

You can find a nice explanation here also.

Example found in docs mentioned below.

  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.

Angular2 and Later

In Angular2 with look at the following example, but its recommended to use Observables with Angular2.

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

}

You can consume that in this way,

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

See the original post here. But Typescript does not support native es6 Promises, if you want to use it, you might need plugin for that.

Additionally here is the promises spec define here.

回答9:

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

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

示例:

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

function doSomethingAsync(value, callback) {
    console.log("Starting async operation for " + value);
    setTimeout(function() {
        console.log("Completing async operation for " + value);
        callback(value * 2);
    }, Math.floor(Math.random() * 200));
}

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

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

平行

您可以全部启动它们,并跟踪期望的回调次数,然后在获得许多回调时使用结果:

.as-console-wrapper {
  max-height: 100% !important;
}

示例:

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

(我们可以省去expecting,而只需使用results.length===theArray.length,但这使我们对< code> theArray 在通话未完成时被更改...)

请注意我们如何使用forEach中的index将结果保存在results中的位置与所涉及条目的位置相同,甚至如果结果顺序混乱(因为异步调用不一定按启动顺序完成)。

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

var theArray = [1, 2, 3];
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
        }
    });
});

function doSomethingAsync(value, callback) {
    console.log("Starting async operation for " + value);
    setTimeout(function() {
        console.log("Completing async operation for " + value);
        callback(value * 2);
    }, Math.floor(Math.random() * 200));
}

示例:

.as-console-wrapper {
  max-height: 100% !important;
}

或者这是一个返回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);
});

当然,如果doSomethingAsync向我们传递了错误,则当出现错误时,我们将使用reject拒绝承诺。) < / p>

示例:

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([1, 2, 3], function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value, callback) {
    console.log("Starting async operation for " + value);
    setTimeout(function() {
        console.log("Completing async operation for " + value);
        callback(value * 2);
    }, Math.floor(Math.random() * 200));
}

(或者,您也可以包装doSomethingAsync并返回一个承诺,然后执行以下操作...)

如果doSomethingAsync给您承诺,您可以使用 Promise.all

.as-console-wrapper {
  max-height: 100% !important;
}

如果您知道doSomethingAsync将忽略第二个和第三个参数,则可以将其直接传递给map(map调用其回调带有三个参数,但大多数人大多数时候只使用第一个参数:

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

示例:

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([1, 2, 3]).then(function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value, callback) {
    console.log("Starting async operation for " + value);
    setTimeout(function() {
        console.log("Completing async operation for " + value);
        callback(value * 2);
    }, Math.floor(Math.random() * 200));
}

请注意,Promise.all使用所有您在解决所有承诺时所提供的所有承诺的结果数组来解决其承诺,或者在首次出现时拒绝其承诺您给它的承诺被拒绝。

系列

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

.as-console-wrapper {
  max-height: 100% !important;
}

(由于我们是连续进行工作,因此我们可以使用results.push(result),因为我们知道我们不会导致结果混乱。在上面我们本可以使用results[index]=result;,但是在以下某些示例中,我们没有要使用的索引。)

示例:

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

(或者再次为doSomethingAsync构建一个包装程序,该包装程序会给您一个承诺并执行以下操作...)

如果doSomethingAsync给您一个承诺,如果您可以使用ES2017 +语法(也许使用 Babel < / a>),则可以使用 async函数 for 等待

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

示例:

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

function doSomethingAsync(value) {
    console.log("Starting async operation for " + value);
    return new Promise(function(resolve) {
        setTimeout(function() {
            console.log("Completing async operation for " + value);
            resolve(value * 2);
        }, Math.floor(Math.random() * 200));
    });
}

如果还不能使用ES2017 +语法,则可以在" Promise reduce"模式(这比通常的Promise reduce更为复杂,因为我们没有将结果从一个传递到下一个,而是将它们的结果收集到一个数组中):

.as-console-wrapper {
  max-height: 100% !important;
}

示例:

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

...使用 ES2015 + arrow则不那么麻烦功能

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([1, 2, 3], function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value, callback) {
    console.log("Starting async operation for " + value);
    setTimeout(function() {
        console.log("Completing async operation for " + value);
        callback(value * 2);
    }, Math.floor(Math.random() * 200));
}

示例:

.as-console-wrapper {
  max-height: 100% !important;
}

A9:

Most of the answers here give useful suggestions for when you have a single async operation, but sometimes, this comes up when you need to do an asynchronous operation for each entry in an array or other list-like structure. The temptation is to do this:

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

Example:

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

function doSomethingAsync(value, callback) {
    console.log("Starting async operation for " + value);
    setTimeout(function() {
        console.log("Completing async operation for " + value);
        callback(value * 2);
    }, Math.floor(Math.random() * 200));
}
.as-console-wrapper {
  max-height: 100% !important;
}

The reason that doesn't work is that the callbacks from doSomethingAsync haven't run yet by the time you're trying to use the results.

So, if you have an array (or list of some kind) and want to do async operations for each entry, you have two options: Do the operations in parallel (overlapping), or in series (one after another in sequence).

Parallel

You can start all of them and keep track of how many callbacks you're expecting, and then use the results when you've gotten that many callbacks:

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

Example:

var theArray = [1, 2, 3];
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
        }
    });
});

function doSomethingAsync(value, callback) {
    console.log("Starting async operation for " + value);
    setTimeout(function() {
        console.log("Completing async operation for " + value);
        callback(value * 2);
    }, Math.floor(Math.random() * 200));
}
.as-console-wrapper {
  max-height: 100% !important;
}

(We could do away with expecting and just use results.length === theArray.length, but that leaves us open to the possibility that theArray is changed while the calls are outstanding...)

Notice how we use the index from forEach to save the result in results in the same position as the entry it relates to, even if the results arrive out of order (since async calls don't necessarily complete in the order in which they were started).

But what if you need to return those results from a function? As the other answers have pointed out, you can't; you have to have your function accept and call a callback (or return a Promise). Here's a callback version:

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

Example:

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([1, 2, 3], function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value, callback) {
    console.log("Starting async operation for " + value);
    setTimeout(function() {
        console.log("Completing async operation for " + value);
        callback(value * 2);
    }, Math.floor(Math.random() * 200));
}
.as-console-wrapper {
  max-height: 100% !important;
}

Or here's a version returning a Promise instead:

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

Of course, if doSomethingAsync passed us errors, we'd use reject to reject the promise when we got an error.)

Example:

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([1, 2, 3]).then(function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value, callback) {
    console.log("Starting async operation for " + value);
    setTimeout(function() {
        console.log("Completing async operation for " + value);
        callback(value * 2);
    }, Math.floor(Math.random() * 200));
}
.as-console-wrapper {
  max-height: 100% !important;
}

(Or alternately, you could make a wrapper for doSomethingAsync that returns a promise, and then do the below...)

If doSomethingAsync gives you a Promise, you can use Promise.all:

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

If you know that doSomethingAsync will ignore a second and third argument, you can just pass it directly to map (map calls its callback with three arguments, but most people only use the first most of the time):

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

Example:

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

function doSomethingAsync(value) {
    console.log("Starting async operation for " + value);
    return new Promise(function(resolve) {
        setTimeout(function() {
            console.log("Completing async operation for " + value);
            resolve(value * 2);
        }, Math.floor(Math.random() * 200));
    });
}
.as-console-wrapper {
  max-height: 100% !important;
}

Note that Promise.all resolves its promise with an array of the results of all of the promises you give it when they are all resolved, or rejects its promise when the first of the promises you give it rejects.

Series

Suppose you don't want the operations to be in parallel? If you want to run them one after another, you need to wait for each operation to complete before you start the next. Here's an example of a function that does that and calls a callback with the result:

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

(Since we're doing the work in series, we can just use results.push(result) since we know we won't get results out of order. In the above we could have used results[index] = result;, but in some of the following examples we don't have an index to use.)

Example:

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([1, 2, 3], function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value, callback) {
    console.log("Starting async operation for " + value);
    setTimeout(function() {
        console.log("Completing async operation for " + value);
        callback(value * 2);
    }, Math.floor(Math.random() * 200));
}
.as-console-wrapper {
  max-height: 100% !important;
}

(Or, again, build a wrapper for doSomethingAsync that gives you a promise and do the below...)

If doSomethingAsync gives you a Promise, if you can use ES2017+ syntax (perhaps with a transpiler like Babel), you can use an async function with for-of and 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);
});

Example:

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

function doSomethingAsync(value) {
    console.log("Starting async operation for " + value);
    return new Promise(function(resolve) {
        setTimeout(function() {
            console.log("Completing async operation for " + value);
            resolve(value * 2);
        }, Math.floor(Math.random() * 200));
    });
}
.as-console-wrapper {
  max-height: 100% !important;
}

If you can't use ES2017+ syntax (yet), you can use a variation on the "Promise reduce" pattern (this is more complex than the usual Promise reduce because we're not passing the result from one into the next, but instead gathering up their results in an array):

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

Example:

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([1, 2, 3]).then(function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value) {
    console.log("Starting async operation for " + value);
    return new Promise(function(resolve) {
        setTimeout(function() {
            console.log("Completing async operation for " + value);
            resolve(value * 2);
        }, Math.floor(Math.random() * 200));
    });
}
.as-console-wrapper {
  max-height: 100% !important;
}

...which is less cumbersome with 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);
});

Example:

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:", results);
});

function doSomethingAsync(value) {
    console.log("Starting async operation for " + value);
    return new Promise(function(resolve) {
        setTimeout(function() {
            console.log("Completing async operation for " + value);
            resolve(value * 2);
        }, Math.floor(Math.random() * 200));
    });
}
.as-console-wrapper {
  max-height: 100% !important;
}

回答10:

看看这个例子:

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正在返回已解决的承诺(在返回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);
  });
})();

A10:

Have a look at this example:

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

As you can see getJoke is returning a resolved promise (it is resolved when returning res.data.value). So you wait until the $http.get request is completed and then console.log(res.joke) is executed (as a normal asynchronous flow).

This is the plnkr:

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

ES6 way (async - await)

(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:

在许多新的JavaScript框架中使用的两种方式的数据绑定商店概念的地方之一将非常适合您...

因此,如果您使用的是 Angular,React 或其他使用两种方式进行数据绑定存储概念的框架,则此问题已得到解决简单来说,第一阶段的结果是undefined,因此在接收数据之前,先得到result=undefined结果,它将被更新并分配给您的Ajax调用响应的新值...

但是,如您在此问题中所提出的那样,如何使用纯 javascript jQuery 做到这一点?

您可以使用回调承诺和最近可观察的来为您处理它,例如,在诺言中,我们具有诸如< code> success()或then(),这些数据将在您准备好数据后执行,与 observable上的回调或 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(); //call happens here
});

有关研究承诺可观察对象的更多信息,这是处理异步内容的新方法。

A11:

This is one of the places which two ways data binding or store concept that's used in many new JavaScript frameworks will work great for you...

So if you are using Angular, React or any other frameworks which do two ways data binding or store concept this issue is simply fixed for you, so in easy word, your result is undefined at the first stage, so you have got result = undefined before you receive the data, then as soon as you get the result, it will be updated and get assigned to the new value which response of your Ajax call...

But how you can do it in pure javascript or jQuery for example as you asked in this question?

You can use a callback, promise and recently observable to handle it for you, for example in promises we have some function like success() or then() which will be executed when your data is ready for you, same with callback or subscribe function on observable.

For example in your case which you are using jQuery, you can do something like this:

$(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(); //call happens here
});

For more information study about promises and observables which are newer ways to do this async stuffs.

回答12:

从异步函数返回值的另一种方法是传入一个对象,该对象将存储来自异步函数的结果。

这里是一个例子:

var async = require("async");

// This wires up result back to the caller
var result = {};
var asyncTasks = [];
asyncTasks.push(function(_callback){
    // some asynchronous operation
    $.ajax({
        url: '...',
        success: function(response) {
            result.response = response;
            _callback();
        }
    });
});

async.parallel(asyncTasks, function(){
    // result is available after performing asynchronous operation
    console.log(result)
    console.log('Done');
});

我正在使用result对象在异步操作期间存储值。这样即使在异步作业之后也可以使用结果。

我经常使用这种方法。我想知道这种方法在通过连续模块将结果接线回去时效果如何。

A12:

Another approach to return a value from an asynchronous function, is to pass in an object that will store the result from the asynchronous function.

Here is an example of the same:

var async = require("async");

// This wires up result back to the caller
var result = {};
var asyncTasks = [];
asyncTasks.push(function(_callback){
    // some asynchronous operation
    $.ajax({
        url: '...',
        success: function(response) {
            result.response = response;
            _callback();
        }
    });
});

async.parallel(asyncTasks, function(){
    // result is available after performing asynchronous operation
    console.log(result)
    console.log('Done');
});

I am using the result object to store the value during the asynchronous operation. This allows the result be available even after the asynchronous job.

I use this approach a lot. I would be interested to know how well this approach works where wiring the result back through consecutive modules is involved.

回答13:

尽管promise和callback在许多情况下都可以正常工作,但表达类似以下内容却很痛苦:

if (!name) {
  name = async1();
}
async2(name);

您最终将经历async1;检查name是否未定义,并相应地调用回调。

async1(name, callback) {
  if (name)
    callback(name)
  else {
    doSomething(callback)
  }
}

async1(name, async2)

虽然在一些小例子中还可以,但是当您遇到很多类似的情况和错误处理时,它会很烦人。

纤维帮助解决问题。

var Fiber = require('fibers')

function async1(container) {
  var current = Fiber.current
  var result
  doSomething(function(name) {
    result = name
    fiber.run()
  })
  Fiber.yield()
  return result
}

Fiber(function() {
  var name
  if (!name) {
    name = async1()
  }
  async2(name)
  // Make any number of async calls from here
}

您可以在此处签出项目。

A13:

While promises and callbacks work fine in many situations, it is a pain in the rear to express something like:

if (!name) {
  name = async1();
}
async2(name);

You'd end up going through async1; check if name is undefined or not and call the callback accordingly.

async1(name, callback) {
  if (name)
    callback(name)
  else {
    doSomething(callback)
  }
}

async1(name, async2)

While it is okay in small examples it gets annoying when you have a lot of similar cases and error handling involved.

Fibers helps in solving the issue.

var Fiber = require('fibers')

function async1(container) {
  var current = Fiber.current
  var result
  doSomething(function(name) {
    result = name
    fiber.run()
  })
  Fiber.yield()
  return result
}

Fiber(function() {
  var name
  if (!name) {
    name = async1()
  }
  async2(name)
  // Make any number of async calls from here
}

You can checkout the project here.

回答14:

我编写的以下示例说明了如何

  • 处理异步HTTP调用;
  • 等待每个API调用的响应;
  • 使用承诺模式;
  • 使用 Promise.all 连接多个HTTP调用的模式;

这个工作示例是独立的。它将定义一个简单的请求对象,该对象使用窗口XMLHttpRequest对象进行调用。它将定义一个简单的函数来等待一堆承诺完成。

上下文。该示例查询 Spotify Web API 端点以搜索 播放列表对象:

[
 "search?type=playlist&q=%22doom%20metal%22",
 "search?type=playlist&q=Adele"
]

对于每个项目,一个新的Promise将触发一个块-ExecutionBlock,解析结果,基于结果数组安排一组新的Promise,即Spotify user的列表对象,并在ExecutionProfileBlock中异步执行新的HTTP调用。

然后,您将看到一个嵌套的Promise结构,该结构允许您生成多个且完全异步的嵌套HTTP调用,并通过Promise.all将来自每个调用子集的结果结合起来。

注意,最近的Spotify search API将要求在请求标头中指定访问令牌:

-H "Authorization: Bearer {your access token}" 

因此,您要运行以下示例,需要将访问令牌放入请求标头中:

var spotifyAccessToken = "YourSpotifyAccessToken";
var console = {
    log: function(s) {
        document.getElementById("console").innerHTML += s + "<br/>"
    }
}

// Simple XMLHttpRequest
// based on https://davidwalsh.name/xmlhttprequest
SimpleRequest = {
    call: function(what, response) {
        var request;
        if (window.XMLHttpRequest) { // Mozilla, Safari, ...
            request = new XMLHttpRequest();
        } else if (window.ActiveXObject) { // Internet Explorer
            try {
                request = new ActiveXObject('Msxml2.XMLHTTP');
            }
            catch (e) {
                try {
                  request = new ActiveXObject('Microsoft.XMLHTTP');
                } catch (e) {}
            }
        }

        // State changes
        request.onreadystatechange = function() {
            if (request.readyState === 4) { // Done
                if (request.status === 200) { // Complete
                    response(request.responseText)
                }
                else
                    response();
            }
        }
        request.open('GET', what, true);
        request.setRequestHeader("Authorization", "Bearer " + spotifyAccessToken);
        request.send(null);
    }
}

//PromiseAll
var promiseAll = function(items, block, done, fail) {
    var self = this;
    var promises = [],
                   index = 0;
    items.forEach(function(item) {
        promises.push(function(item, i) {
            return new Promise(function(resolve, reject) {
                if (block) {
                    block.apply(this, [item, index, resolve, reject]);
                }
            });
        }(item, ++index))
    });
    Promise.all(promises).then(function AcceptHandler(results) {
        if (done) done(results);
    }, function ErrorHandler(error) {
        if (fail) fail(error);
    });
}; //promiseAll

// LP: deferred execution block
var ExecutionBlock = function(item, index, resolve, reject) {
    var url = "https://api.spotify.com/v1/"
    url += item;
    console.log( url )
    SimpleRequest.call(url, function(result) {
        if (result) {

            var profileUrls = JSON.parse(result).playlists.items.map(function(item, index) {
                return item.owner.href;
            })
            resolve(profileUrls);
        }
        else {
            reject(new Error("call error"));
        }
    })
}

arr = [
    "search?type=playlist&q=%22doom%20metal%22",
    "search?type=playlist&q=Adele"
]

promiseAll(arr, function(item, index, resolve, reject) {
    console.log("Making request [" + index + "]")
    ExecutionBlock(item, index, resolve, reject);
}, function(results) { // Aggregated results

    console.log("All profiles received " + results.length);
    //console.log(JSON.stringify(results[0], null, 2));

    ///// promiseall again

    var ExecutionProfileBlock = function(item, index, resolve, reject) {
        SimpleRequest.call(item, function(result) {
            if (result) {
                var obj = JSON.parse(result);
                resolve({
                    name: obj.display_name,
                    followers: obj.followers.total,
                    url: obj.href
                });
            } //result
        })
    } //ExecutionProfileBlock

    promiseAll(results[0], function(item, index, resolve, reject) {
        //console.log("Making request [" + index + "] " + item)
        ExecutionProfileBlock(item, index, resolve, reject);
    }, function(results) { // aggregated results
        console.log("All response received " + results.length);
        console.log(JSON.stringify(results, null, 2));
    }

    , function(error) { // Error
        console.log(error);
    })

    /////

  },
  function(error) { // Error
      console.log(error);
  });

我已经广泛讨论了此解决方案此处

A14:

The following example I have written shows how to

  • Handle asynchronous HTTP calls;
  • Wait for response from each API call;
  • Use Promise pattern;
  • Use Promise.all pattern to join multiple HTTP calls;

This working example is self-contained. It will define a simple request object that uses the window XMLHttpRequest object to make calls. It will define a simple function to wait for a bunch of promises to be completed.

Context. The example is querying the Spotify Web API endpoint in order to search for playlist objects for a given set of query strings:

[
 "search?type=playlist&q=%22doom%20metal%22",
 "search?type=playlist&q=Adele"
]

For each item, a new Promise will fire a block - ExecutionBlock, parse the result, schedule a new set of promises based on the result array, that is a list of Spotify user objects and execute the new HTTP call within the ExecutionProfileBlock asynchronously.

You can then see a nested Promise structure, that lets you spawn multiple and completely asynchronous nested HTTP calls, and join the results from each subset of calls through Promise.all.

NOTE Recent Spotify search APIs will require an access token to be specified in the request headers:

-H "Authorization: Bearer {your access token}" 

So, you to run the following example you need to put your access token in the request headers:

var spotifyAccessToken = "YourSpotifyAccessToken";
var console = {
    log: function(s) {
        document.getElementById("console").innerHTML += s + "<br/>"
    }
}

// Simple XMLHttpRequest
// based on https://davidwalsh.name/xmlhttprequest
SimpleRequest = {
    call: function(what, response) {
        var request;
        if (window.XMLHttpRequest) { // Mozilla, Safari, ...
            request = new XMLHttpRequest();
        } else if (window.ActiveXObject) { // Internet Explorer
            try {
                request = new ActiveXObject('Msxml2.XMLHTTP');
            }
            catch (e) {
                try {
                  request = new ActiveXObject('Microsoft.XMLHTTP');
                } catch (e) {}
            }
        }

        // State changes
        request.onreadystatechange = function() {
            if (request.readyState === 4) { // Done
                if (request.status === 200) { // Complete
                    response(request.responseText)
                }
                else
                    response();
            }
        }
        request.open('GET', what, true);
        request.setRequestHeader("Authorization", "Bearer " + spotifyAccessToken);
        request.send(null);
    }
}

//PromiseAll
var promiseAll = function(items, block, done, fail) {
    var self = this;
    var promises = [],
                   index = 0;
    items.forEach(function(item) {
        promises.push(function(item, i) {
            return new Promise(function(resolve, reject) {
                if (block) {
                    block.apply(this, [item, index, resolve, reject]);
                }
            });
        }(item, ++index))
    });
    Promise.all(promises).then(function AcceptHandler(results) {
        if (done) done(results);
    }, function ErrorHandler(error) {
        if (fail) fail(error);
    });
}; //promiseAll

// LP: deferred execution block
var ExecutionBlock = function(item, index, resolve, reject) {
    var url = "https://api.spotify.com/v1/"
    url += item;
    console.log( url )
    SimpleRequest.call(url, function(result) {
        if (result) {

            var profileUrls = JSON.parse(result).playlists.items.map(function(item, index) {
                return item.owner.href;
            })
            resolve(profileUrls);
        }
        else {
            reject(new Error("call error"));
        }
    })
}

arr = [
    "search?type=playlist&q=%22doom%20metal%22",
    "search?type=playlist&q=Adele"
]

promiseAll(arr, function(item, index, resolve, reject) {
    console.log("Making request [" + index + "]")
    ExecutionBlock(item, index, resolve, reject);
}, function(results) { // Aggregated results

    console.log("All profiles received " + results.length);
    //console.log(JSON.stringify(results[0], null, 2));

    ///// promiseall again

    var ExecutionProfileBlock = function(item, index, resolve, reject) {
        SimpleRequest.call(item, function(result) {
            if (result) {
                var obj = JSON.parse(result);
                resolve({
                    name: obj.display_name,
                    followers: obj.followers.total,
                    url: obj.href
                });
            } //result
        })
    } //ExecutionProfileBlock

    promiseAll(results[0], function(item, index, resolve, reject) {
        //console.log("Making request [" + index + "] " + item)
        ExecutionProfileBlock(item, index, resolve, reject);
    }, function(results) { // aggregated results
        console.log("All response received " + results.length);
        console.log(JSON.stringify(results, null, 2));
    }

    , function(error) { // Error
        console.log(error);
    })

    /////

  },
  function(error) { // Error
      console.log(error);
  });
<div id="console" />

I have extensively discussed this solution here.

回答15:

简单的答案是,您必须实施这样的回调:

function callback(response) {
    // Here you can do what ever you want with the response object.
    console.log(response);
}

$.ajax({
    url: "...",
    success: callback
});

A15:

Short answer is, you have to implement a callback like this:

function callback(response) {
    // Here you can do what ever you want with the response object.
    console.log(response);
}

$.ajax({
    url: "...",
    success: callback
});

回答16:

这是我们在应对JavaScript的"奥秘"时面临的一个非常普遍的问题。让我今天尝试揭开这个谜团的神秘面纱。

让我们从一个简单的JavaScript函数开始:

function foo(){
// do something 
 return 'wohoo';
}

let bar = foo(); // bar is 'wohoo' here

这是一个简单的同步函数调用(其中,每一行代码均按顺序在下一行之前"完成其工作"),结果与预期的相同。

现在,通过在函数中引入很少的延迟,让我们增加一点扭曲,以便所有代码行都不会按顺序"完成"。因此,它将模拟功能的异步行为:

function foo(){
 setTimeout( ()=>{
   return 'wohoo';
  }, 1000 )
}

let bar = foo() // bar is undefined here

那么,您去吧,这种延迟破坏了我们期望的功能!但是到底发生了什么?好吧,如果您看一下代码,这实际上是很合逻辑的。函数foo()在执行时不返回任何内容(因此返回的值是undefined),但是它确实启动了一个计时器,该计时器在1秒后执行一个函数以返回" wohoo" '。但是正如您所看到的,分配给bar的值是foo()立即返回的内容,而不是随后出现的其他任何内容。

那么,我们如何解决这个问题?

让我们问一个功能 PROMISE 。 Promise确实是关于它的含义:它意味着该函数保证您提供将来获得的任何输出。因此,让我们来看一下上面的小问题:

function foo(){
   return new Promise( (resolve, reject) => { // I want foo() to PROMISE me something
    setTimeout ( function(){ 
      // promise is RESOLVED , when execution reaches this line of code
       resolve('wohoo')// After 1 second, RESOLVE the promise with value 'wohoo'
    }, 1000 )
  })
}

let bar ; 
foo().then( res => {
 bar = res;
 console.log(bar) // will print 'wohoo'
});

因此,摘要是-为处理基于ajax的调用等异步功能,您可以使用promise来resolve值(您打算返回)。因此,简而言之,在异步函数中,您解析值而不是返回

更新(带有异步/等待的承诺)

除了使用then/catch来处理诺言外,还有另一种方法。这个想法是识别一个异步函数,然后等待promise 解析,然后再转到下一行代码。它仍然只是底层的promises,但是具有不同的语法方法。为了使事情更清楚,您可以在下面找到一个比较:

然后/捕获版本:

function saveUsers(){
     getUsers()
      .then(users => {
         saveSomewhere(users);
      })
      .catch(err => {
         throw err;
       })
 }

异步/等待版本:

  async function saveUsers(){
     try{
        let users = await getUsers()
        saveSomewhere(users);
     }
     catch(err){
        throw err;
     }
  }

A16:

It's a very common issue we face while struggling with the 'mysteries' of JavaScript. Let me try demystifying this mystery today.

Let's start with a simple JavaScript function:

function foo(){
// do something 
 return 'wohoo';
}

let bar = foo(); // bar is 'wohoo' here

That's a simple synchronous function call (where each line of code is 'finished with its job' before the next one in sequence), and the result is same as expected.

Now let's add a bit of twist, by introducing little delay in our function, so that all lines of code are not 'finished' in sequence. Thus, it will emulate the asynchronous behavior of function :

function foo(){
 setTimeout( ()=>{
   return 'wohoo';
  }, 1000 )
}

let bar = foo() // bar is undefined here

So there you go, that delay just broke the functionality we expected! But what exactly happened ? Well, it's actually pretty logical if you look at the code. the function foo(), upon execution, returns nothing (thus returned value is undefined), but it does start a timer, which executes a function after 1s to return 'wohoo'. But as you can see, the value that's assigned to bar is the immediately returned stuff from foo(), not anything else that comes later.

So, how do we tackle this issue?

Let's ask our function for a PROMISE. Promise is really about what it means : it means that the function guarantees you to provide with any output it gets in future. so let's see it in action for our little problem above :

function foo(){
   return new Promise( (resolve, reject) => { // I want foo() to PROMISE me something
    setTimeout ( function(){ 
      // promise is RESOLVED , when execution reaches this line of code
       resolve('wohoo')// After 1 second, RESOLVE the promise with value 'wohoo'
    }, 1000 )
  })
}

let bar ; 
foo().then( res => {
 bar = res;
 console.log(bar) // will print 'wohoo'
});

Thus, the summary is - to tackle the asynchronous functions like ajax based calls etc., you can use a promise to resolve the value (which you intend to return). Thus, in short you resolve value instead of returning, in asynchronous functions.

UPDATE (Promises with async/await)

Apart from using then/catch to work with promises, there exists one more approach. The idea is to recognize an asynchronous function and then wait for the promises to resolve, before moving to the next line of code. It's still just the promises under the hood, but with a different syntactical approach. To make things clearer, you can find a comparison below:

then/catch version:

function saveUsers(){
     getUsers()
      .then(users => {
         saveSomewhere(users);
      })
      .catch(err => {
         throw err;
       })
 }

async/await version:

  async function saveUsers(){
     try{
        let users = await getUsers()
        saveSomewhere(users);
     }
     catch(err){
        throw err;
     }
  }

回答17:

2017年答案:您现在可以在每个当前的浏览器和节点中完全执行所需的操作

这很简单:

  • 退还承诺
  • 使用'await',它将告诉JavaScript等待将承诺解析为一个值(例如HTTP响应)
  • 添加"异步" 关键字到父函数

这是您代码的有效版本:

(async function(){

var response = await superagent.get('...')
console.log(response)

})()

所有当前浏览器和节点8中都支持await

A17:

2017 answer: you can now do exactly what you want in every current browser and node

This is quite simple:

  • Return a Promise
  • Use the 'await', which will tell JavaScript to await the promise to be resolved into a value (like the HTTP response)
  • Add the 'async' keyword to the parent function

Here's a working version of your code:

(async function(){

var response = await superagent.get('...')
console.log(response)

})()

await is supported in all current browsers and node 8

回答18:

Js是单线程的。

浏览器可以分为三个部分:

1)事件循环

2)Web API

3)事件队列

事件循环永远运行,即无限循环。事件队列是将所有功能推送到某个事件(例如:点击)的地方,它是逐个执行队列,并放入执行该功能的事件循环中。在第一个函数执行完之后,自己为下一个做准备。这意味着一个函数的执行要等到事件循环中的队列中的函数执行完之后,才开始执行。

现在让我们认为我们在队列中推送了两个函数,一个是从服务器获取数据,另一个是利用该数据。我们先在队列中推送了serverRequest()函数,然后是utiliseData()函数。 serverRequest函数进入事件循环并调用服务器,因为我们永远不知道从服务器获取数据将花费多少时间,因此预计此过程将花费一些时间,因此我们忙于事件循环,从而挂起了页面,这就是Web API发挥作用,它将事件循环中的此功能带入服务器,并与服务器释放事件循环,以便我们可以从队列中执行下一个功能。队列中的下一个功能是utiliseData(),它进入了循环,但由于没有可用数据,它进入了浪费,下一个功能的执行一直持续到队列结束。(这称为异步调用,即我们可以做其他事情直到获得数据)

假设我们的serverRequest()函数在代码中有一个return语句,当我们从服务器Web API取回数据时,它将在队列末尾将其压入队列。由于它在队列末尾被推送,因此我们无法利用其数据,因为队列中没有任何函数可以利用这些数据。因此,不可能从异步调用中返回某些内容。

因此,解决方案是回调承诺

来自此处答案之一的图像,正确解释了回调的用法... 我们将函数(利用服务器返回的数据的函数)提供给函数调用服务器。

 CallBack

 function doAjax(callbackFunc, method, url) {
  var xmlHttpReq = new XMLHttpRequest();
  xmlHttpReq.open(method, url);
  xmlHttpReq.onreadystatechange = function() {

      if (xmlHttpReq.readyState == 4 && xmlHttpReq.status == 200) {
        callbackFunc(xmlHttpReq.responseText);
      }


  }
  xmlHttpReq.send(null);

}

在我的代码中,它被称为

function loadMyJson(categoryValue){
  if(categoryValue==="veg")
  doAjax(print,"GET","http://localhost:3004/vegetables");
  else if(categoryValue==="fruits")
  doAjax(print,"GET","http://localhost:3004/fruits");
  else 
  console.log("Data not found");
}

Javscript.info回调

A18:

Js is a single threaded.

Browser can be divided into three parts:

1)Event Loop

2)Web API

3)Event Queue

Event Loop runs for forever i.e kind of infinite loop.Event Queue is where all your function are pushed on some event(example:click) this is one by one carried out of queue and put into Event loop which execute this function and prepares it self for next one after first one is executed.This means Execution of one function doesn't starts till the function before it in queue is executed in event loop.

Now let us think we pushed two functions in a queue one is for getting a data from server and another utilises that data.We pushed the serverRequest() function in queue first then utiliseData() function. serverRequest function goes in event loop and makes a call to server as we never know how much time it will take to get data from server so this process is expected to take time and so we busy our event loop thus hanging our page, that's where Web API come into role it take this function from event loop and deals with server making event loop free so that we can execute next function from queue.The next function in queue is utiliseData() which goes in loop but because of no data available it goes waste and execution of next function continues till end of the queue.(This is called Async calling i.e we can do something else till we get data)

Let suppose our serverRequest() function had a return statement in a code, when we get back data from server Web API will push it in queue at the end of queue. As it get pushed at end in queue we cannot utilise its data as there is no function left in our queue to utilise this data.Thus it is not possible to return something from Async Call.

Thus Solution to this is callback or promise.

A Image from one of the answers here, Correctly explains callback use... We give our function(function utilising data returned from server) to function calling server.

 function doAjax(callbackFunc, method, url) {
  var xmlHttpReq = new XMLHttpRequest();
  xmlHttpReq.open(method, url);
  xmlHttpReq.onreadystatechange = function() {

      if (xmlHttpReq.readyState == 4 && xmlHttpReq.status == 200) {
        callbackFunc(xmlHttpReq.responseText);
      }


  }
  xmlHttpReq.send(null);

}

In my Code it is called as

function loadMyJson(categoryValue){
  if(categoryValue==="veg")
  doAjax(print,"GET","http://localhost:3004/vegetables");
  else if(categoryValue==="fruits")
  doAjax(print,"GET","http://localhost:3004/fruits");
  else 
  console.log("Data not found");
}

Javscript.info callback

回答19:

您可以使用此自定义库(使用Promise编写)来进行远程调用。

function $http(apiConfig) {
    return new Promise(function (resolve, reject) {
        var client = new XMLHttpRequest();
        client.open(apiConfig.method, apiConfig.url);
        client.send();
        client.onload = function () {
            if (this.status >= 200 && this.status < 300) {
                // Performs the function "resolve" when this.status is equal to 2xx.
                // Your logic here.
                resolve(this.response);
            }
            else {
                // Performs the function "reject" when this.status is different than 2xx.
                reject(this.statusText);
            }
        };
        client.onerror = function () {
            reject(this.statusText);
        };
    });
}

简单用法示例:

$http({
    method: 'get',
    url: 'google.com'
}).then(function(response) {
    console.log(response);
}, function(error) {
    console.log(error)
});

A19:

You can use this custom library (written using Promise) to make a remote call.

function $http(apiConfig) {
    return new Promise(function (resolve, reject) {
        var client = new XMLHttpRequest();
        client.open(apiConfig.method, apiConfig.url);
        client.send();
        client.onload = function () {
            if (this.status >= 200 && this.status < 300) {
                // Performs the function "resolve" when this.status is equal to 2xx.
                // Your logic here.
                resolve(this.response);
            }
            else {
                // Performs the function "reject" when this.status is different than 2xx.
                reject(this.statusText);
            }
        };
        client.onerror = function () {
            reject(this.statusText);
        };
    });
}

Simple usage example:

$http({
    method: 'get',
    url: 'google.com'
}).then(function(response) {
    console.log(response);
}, function(error) {
    console.log(error)
});

回答20:

另一种解决方案是通过顺序执行器 nsynjs 执行代码。

如果底层功能已实现

nsynjs将顺序评估所有promise,并将promise结果放入data属性中:

function synchronousCode() {

    var getURL = function(url) {
        return window.fetch(url).data.text().data;
    };
    
    var url = 'https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js';
    console.log('received bytes:',getURL(url).length);
    
};

nsynjs.run(synchronousCode,{},function(){
    console.log('synchronousCode done');
});

如果底层功能不明确

步骤1。将具有回调的函数包装到nsynjs感知的包装器中(如果它具有承诺版本,则可以跳过此步骤):

<script src="https://rawgit.com/amaksr/nsynjs/master/nsynjs.js"></script>

第2步。将同步逻辑放入功能中:

var ajaxGet = function (ctx,url) {
    var res = {};
    var ex;
    $.ajax(url)
    .done(function (data) {
        res.data = data;
    })
    .fail(function(e) {
        ex = e;
    })
    .always(function() {
        ctx.resume(ex);
    });
    return res;
};
ajaxGet.nsynjsHasCallback = true;

第3步。通过nsynjs同步运行函数:

function process() {
    console.log('got data:', ajaxGet(nsynjsCtx, "data/file1.json").data);
}

Nsynjs将逐步评估所有运算符和表达式,以防某些慢速函数的结果尚未就绪时暂停执行。

更多示例在这里: https://github.com/amaksr/nsynjs/tree/大师/示例

A20:

Another solution is to execute code via sequential executor nsynjs.

If underlying function is promisified

nsynjs will evaluate all promises sequentially, and put promise result into data property:

function synchronousCode() {

    var getURL = function(url) {
        return window.fetch(url).data.text().data;
    };
    
    var url = 'https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js';
    console.log('received bytes:',getURL(url).length);
    
};

nsynjs.run(synchronousCode,{},function(){
    console.log('synchronousCode done');
});
<script src="https://rawgit.com/amaksr/nsynjs/master/nsynjs.js"></script>

If underlying function is not promisified

Step 1. Wrap function with callback into nsynjs-aware wrapper (if it has promisified version, you can skip this step):

var ajaxGet = function (ctx,url) {
    var res = {};
    var ex;
    $.ajax(url)
    .done(function (data) {
        res.data = data;
    })
    .fail(function(e) {
        ex = e;
    })
    .always(function() {
        ctx.resume(ex);
    });
    return res;
};
ajaxGet.nsynjsHasCallback = true;

Step 2. Put synchronous logic into function:

function process() {
    console.log('got data:', ajaxGet(nsynjsCtx, "data/file1.json").data);
}

Step 3. Run function in synchronous manner via nsynjs:

nsynjs.run(process,this,function () {
    console.log("synchronous function finished");
});

Nsynjs will evaluate all operators and expressions step-by-step, pausing execution in case if result of some slow function is not ready.

More examples here: https://github.com/amaksr/nsynjs/tree/master/examples

回答21:

ECMAScript 6具有"生成器",使您可以轻松地以异步样式进行编程。

function* myGenerator() {
    const callback = yield;
    let [response] = yield $.ajax("https://stackoverflow.com", {complete: callback});
    console.log("response is:", response);

    // examples of other things you can do
    yield setTimeout(callback, 1000);
    console.log("it delayed for 1000ms");
    while (response.statusText === "error") {
        [response] = yield* anotherGenerator();
    }
}

要运行上述代码,请执行以下操作:

const gen = myGenerator(); // Create generator
gen.next(); // Start it
gen.next((...args) => gen.next([...args])); // Set its callback function

如果需要使用不支持ES6的浏览器,则可以通过Babel或闭包编译器运行代码以生成ECMAScript 5。

回调...args被包装在数组中,并在您读取它们时进行了结构分解,以便该模式可以处理具有多个参数的回调。例如,使用节点fs

const [err, data] = yield fs.readFile(filePath, "utf-8", callback);

A21:

ECMAScript 6 has 'generators' which allow you to easily program in an asynchronous style.

function* myGenerator() {
    const callback = yield;
    let [response] = yield $.ajax("https://stackoverflow.com", {complete: callback});
    console.log("response is:", response);

    // examples of other things you can do
    yield setTimeout(callback, 1000);
    console.log("it delayed for 1000ms");
    while (response.statusText === "error") {
        [response] = yield* anotherGenerator();
    }
}

To run the above code you do this:

const gen = myGenerator(); // Create generator
gen.next(); // Start it
gen.next((...args) => gen.next([...args])); // Set its callback function

If you need to target browsers that don't support ES6 you can run the code through Babel or closure-compiler to generate ECMAScript 5.

The callback ...args are wrapped in an array and destructured when you read them so that the pattern can cope with callbacks that have multiple arguments. For example with node fs:

const [err, data] = yield fs.readFile(filePath, "utf-8", callback);

回答22:

以下是处理异步请求的一些方法:

  1. 浏览器承诺对象
  2. Q -JavaScript的承诺库
  3. A + Promises.js
  4. jQuery延迟
  5. XMLHttpRequest API
  6. 使用回调概念-作为第一个答案的实现

示例:jQuery推迟实现以处理多个请求

var App = App || {};

App = {
    getDataFromServer: function(){

      var self = this,
                 deferred = $.Deferred(),
                 requests = [];

      requests.push($.getJSON('request/ajax/url/1'));
      requests.push($.getJSON('request/ajax/url/2'));

      $.when.apply(jQuery, requests).done(function(xhrResponse) {
        return deferred.resolve(xhrResponse.result);
      });
      return deferred;
    },

    init: function(){

        this.getDataFromServer().done(_.bind(function(resp1, resp2) {

           // Do the operations which you wanted to do when you
           // get a response from Ajax, for example, log response.
        }, this));
    }
};
App.init();

A22:

Here are some approaches to work with asynchronous requests:

  1. Browser Promise object
  2. Q - A promise library for JavaScript
  3. A+ Promises.js
  4. jQuery deferred
  5. XMLHttpRequest API
  6. Using callback concept - As implementation in first answer

Example: jQuery deferred implementation to work with multiple requests

var App = App || {};

App = {
    getDataFromServer: function(){

      var self = this,
                 deferred = $.Deferred(),
                 requests = [];

      requests.push($.getJSON('request/ajax/url/1'));
      requests.push($.getJSON('request/ajax/url/2'));

      $.when.apply(jQuery, requests).done(function(xhrResponse) {
        return deferred.resolve(xhrResponse.result);
      });
      return deferred;
    },

    init: function(){

        this.getDataFromServer().done(_.bind(function(resp1, resp2) {

           // Do the operations which you wanted to do when you
           // get a response from Ajax, for example, log response.
        }, this));
    }
};
App.init();

回答23:

简短答案:您的foo()方法立即返回,而$ajax()调用在该函数之后异步执行返回。问题是异步调用返回后如何或在何处存储结果。

此线程中提供了几种解决方案。也许最简单的方法是将对象传递给foo()方法,并在异步调用完成后将结果存储在该对象的成员中。

function foo(result) {
    $.ajax({
        url: '...',
        success: function(response) {
            result.response = response;   // Store the async result
        }
    });
}

var result = { response: null };   // Object to hold the async result
foo(result);                       // Returns before the async completes

请注意,对foo()的调用仍然不会返回任何有用的信息。但是,异步调用的结果现在将存储在result.response中。

A23:

Short answer: Your foo() method returns immediately, while the $ajax() call executes asynchronously after the function returns. The problem is then how or where to store the results retrieved by the async call once it returns.

Several solutions have been given in this thread. Perhaps the easiest way is to pass an object to the foo() method, and to store the results in a member of that object after the async call completes.

function foo(result) {
    $.ajax({
        url: '...',
        success: function(response) {
            result.response = response;   // Store the async result
        }
    });
}

var result = { response: null };   // Object to hold the async result
foo(result);                       // Returns before the async completes

Note that the call to foo() will still return nothing useful. However, the result of the async call will now be stored in result.response.

回答24:

我们发现自己处于一个宇宙中,这个宇宙似乎沿着我们称为"时间"的维度前进。我们并不真正了解现在的时间,但是我们已经开发出抽象和词汇,让我们进行推理和讨论:"过去","现在","未来","之前","之后"。

我们构建的计算机系统越来越多地将时间作为重要方面。某些事情会在将来发生。然后,在这些最初的事情最终发生之后,还需要发生其他事情。这是称为"异步性"的基本概念。在我们这个日益网络化的世界中,异步的最常见情况是等待某个远程系统响应某些请求。

考虑一个例子。您打电话给送牛奶的人,点些牛奶。到时,您想将其放入咖啡中。您现在不能将牛奶放入咖啡中,因为现在还没有。您必须等待它来,然后再将其放入咖啡中。换句话说,以下操作无效:

var milk = order_milk();
put_in_coffee(milk);

因为JS无法执行put_in_coffee,所以它无法知道需要等待完成order_milk。换句话说,它不知道order_milk异步的东西-直到将来某个时候才会产生牛奶。 JS和其他声明性语言无需等待即可执行另一个语句。

针对此问题的经典JS方法利用了JS支持将函数作为可传递的一流对象这一事实,这是将函数作为参数传递给异步请求,然后在异步请求时调用该函数。它在将来的某个时候完成了任务。那就是"回调"方法。看起来像这样:

order_milk(put_in_coffee);

order_milk开始,订购牛奶,然后,只有当牛奶到达时,它才会调用put_in_coffee

这种回调方法的问题在于,它污染了使用return报告结果的函数的正常语义;相反,函数不得通过调用作为参数给出的回调来报告其结果。同样,在处理较长的事件序列时,此方法可能很快变得笨拙。例如,假设我要等待牛奶放入咖啡中,然后再执行第三步,即喝咖啡。我最终需要写这样的东西:

order_milk(function(milk) { put_in_coffee(milk, drink_coffee); }

我要传递给put_in_coffee的牛奶和放入牛奶后要执行的动作(drink_coffee)。这样的代码变得难以编写,读取和调试。

在这种情况下,我们可以将问题中的代码重写为:

var answer;
$.ajax('/foo.json') . done(function(response) {
  callback(response.data);
});

function callback(data) {
  console.log(data);
}

输入承诺

这是"承诺"概念的动机,"承诺"是一种特殊的价值类型,代表某种未来异步结果。它可以表示已经发生的事情,或者将来会发生的事情,或者可能永远不会发生的事情。承诺有一个名为then的方法,当实现了promise表示的结果时,您将向该方法传递要执行的动作。

对于我们的牛奶和咖啡,我们设计order_milk来返回牛奶到达的承诺,然后将put_in_coffee指定为then操作,如下所示:

order_milk() . then(put_in_coffee)

这样做的一个优点是我们可以将它们串在一起以创建将来出现的序列("链接"):

order_milk() . then(put_in_coffee) . then(drink_coffee)

让我们对您的特定问题应用承诺。我们将请求逻辑包装在一个函数中,该函数返回一个Promise:

function get_data() {
  return $.ajax('/foo.json');
}

实际上,我们所做的只是在对$.ajax的调用中添加了return。之所以可行,是因为jQuery的$.ajax已经返回了一种类似于promise的东西。 (在实践中,我们不愿赘述,而是希望包装此调用以便返回真实的诺言,或者使用其他替代方法来做到这一点。)现在,如果我们想加载文件并等待其完成,然后执行一些操作,我们可以简单地说

get_data() . then(do_something)

例如

get_data() . 
  then(function(data) { console.log(data); });

使用promise时,我们最终将许多函数传递给然后,因此使用更紧凑的ES6样式箭头功能通常会有所帮助:

get_data() . 
  then(data => console.log(data));

async关键字

但是,如果必须以一种同步方式编写代码,而如果以异步方式编写另一种方法,则仍然存在一些含糊的不满。对于同步,我们写

a();
b();

但是如果a是异步的,则必须保证我们必须编写

a() . then(b);

我们在上面说过:" JS无法知道它需要等待,以便在执行第二个调用之前先完成它"。如果有某种方式告诉JS,那不是很好吗?事实证明,在特殊类型的函数(称为"异步"函数)中使用了await关键字。此功能是即将推出的ES版本的一部分,但已提供适当的预设,在诸如Babel之类的编译器中已可用。这使我们可以简单地编写

async function morning_routine() {
  var milk   = await order_milk();
  var coffee = await put_in_coffee(milk);
  await drink(coffee);
}

就您而言,您将可以编写类似

的内容
async function foo() {
  data = await get_data();
  console.log(data);
}

A24:

We find ourselves in a universe which appears to progress along a dimension we call "time". We don't really understand what time is, but we have developed abstractions and vocabulary that let us reason and talk about it: "past", "present", "future", "before", "after".

The computer systems we build--more and more--have time as an important dimension. Certain things are set up to happen in the future. Then other things need to happen after those first things eventually occur. This is the basic notion called "asynchronicity". In our increasingly networked world, the most common case of asynchronicity is waiting for some remote system to respond to some request.

Consider an example. You call the milkman and order some milk. When it comes, you want to put it in your coffee. You can't put the milk in your coffee right now, because it is not here yet. You have to wait for it to come before putting it in your coffee. In other words, the following won't work:

var milk = order_milk();
put_in_coffee(milk);

Because JS has no way to know that it needs to wait for order_milk to finish before it executes put_in_coffee. In other words, it does not know that order_milk is asynchronous--is something that is not going to result in milk until some future time. JS, and other declarative languages execute one statement after another without waiting.

The classic JS approach to this problem, taking advantage of the fact that JS supports functions as first-class objects which can be passed around, is to pass a function as a parameter to the asynchronous request, which it will then invoke when it has completed its task sometime in the future. That is the "callback" approach. It looks like this:

order_milk(put_in_coffee);

order_milk kicks off, orders the milk, then, when and only when it arrives, it invokes put_in_coffee.

The problem with this callback approach is that it pollutes the normal semantics of a function reporting its result with return; instead, functions must not reports their results by calling a callback given as a parameter. Also, this approach can rapidly become unwieldy when dealing with longer sequences of events. For example, let's say that I want to wait for the milk to be put in the coffee, and then and only then perform a third step, namely drinking the coffee. I end up needing to write something like this:

order_milk(function(milk) { put_in_coffee(milk, drink_coffee); }

where I am passing to put_in_coffee both the milk to put in it, and also the action (drink_coffee) to execute once the milk has been put in. Such code becomes hard to write, and read, and debug.

In this case, we could rewrite the code in the question as:

var answer;
$.ajax('/foo.json') . done(function(response) {
  callback(response.data);
});

function callback(data) {
  console.log(data);
}

Enter promises

This was the motivation for the notion of a "promise", which is a particular type of value which represents a future or asynchronous outcome of some sort. It can represent something that already happened, or that is going to happen in the future, or might never happen at all. Promises have a single method, named then, to which you pass an action to be executed when the outcome the promise represents has been realized.

In the case of our milk and coffee, we design order_milk to return a promise for the milk arriving, then specify put_in_coffee as a then action, as follows:

order_milk() . then(put_in_coffee)

One advantage of this is that we can string these together to create sequences of future occurrences ("chaining"):

order_milk() . then(put_in_coffee) . then(drink_coffee)

Let's apply promises to your particular problem. We will wrap our request logic inside a function, which returns a promise:

function get_data() {
  return $.ajax('/foo.json');
}

Actually, all we've done is added a return to the call to $.ajax. This works because jQuery's $.ajax already returns a kind of promise-like thing. (In practice, without getting into details, we would prefer to wrap this call so as for return a real promise, or use some alternative to $.ajax that does so.) Now, if we want to load the file and wait for it to finish and then do something, we can simply say

get_data() . then(do_something)

for instance,

get_data() . 
  then(function(data) { console.log(data); });

When using promises, we end up passing lots of functions into then, so it's often helpful to use the more compact ES6-style arrow functions:

get_data() . 
  then(data => console.log(data));

The async keyword

But there's still something vaguely dissatisfying about having to write code one way if synchronous and a quite different way if asynchronous. For synchronous, we write

a();
b();

but if a is asynchronous, with promises we have to write

a() . then(b);

Above, we said, "JS has no way to know that it needs to wait for the first call to finish before it executes the second". Wouldn't it be nice if there was some way to tell JS that? It turns out that there is--the await keyword, used inside a special type of function called an "async" function. This feature is part of the upcoming version of ES but is already available in transpilers such as Babel given the right presets. This allows us to simply write

async function morning_routine() {
  var milk   = await order_milk();
  var coffee = await put_in_coffee(milk);
  await drink(coffee);
}

In your case, you would be able to write something like

async function foo() {
  data = await get_data();
  console.log(data);
}

回答25:

foo()成功内使用callback()函数。以这种方式尝试。它简单易懂。

var lat = "";
var lon = "";
function callback(data) {
    lat = data.lat;
    lon = data.lon;
}
function getLoc() {
    var url = "http://ip-api.com/json"
    $.getJSON(url, function(data) {
        callback(data);
    });
}

getLoc();

A25:

Use a callback() function inside the foo() success. Try in this way. It is simple and easy to understand.

var lat = "";
var lon = "";
function callback(data) {
    lat = data.lat;
    lon = data.lon;
}
function getLoc() {
    var url = "http://ip-api.com/json"
    $.getJSON(url, function(data) {
        callback(data);
    });
}

getLoc();

回答26:

当然,有很多方法,例如同步请求,promise,但是根据我的经验,我认为您应该使用回调方法。 Java的异步行为是很自然的。因此,您的代码段可以重写一些:

function foo() {
    var result;

    $.ajax({
        url: '...',
        success: function(response) {
            myCallback(response);
        }
    });

    return result;
}

function myCallback(response) {
    // Does something.
}

A26:

Of course there are many approaches like synchronous request, promise, but from my experience I think you should use the callback approach. It's natural to asynchronous behavior of Javascript. So, your code snippet can be rewrite a little different:

function foo() {
    var result;

    $.ajax({
        url: '...',
        success: function(response) {
            myCallback(response);
        }
    });

    return result;
}

function myCallback(response) {
    // Does something.
}

回答27:

问题是:

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

可以解释为:

如何使异步代码看起来像同步

解决方案是避免回调,并结合使用 Promises async / await

我想举一个Ajax请求的例子。

(尽管它可以用Javascript编写,但我更喜欢用Python编写,并使用 Transcrypt编译为Javascript 。就足够清楚了。)

首先要启用JQuery使用,以使$作为S可用:

__pragma__ ('alias', 'S', '$')

定义一个函数,该函数返回一个 Promise (在这种情况下为Ajax调用):

def read(url: str):
    deferred = S.Deferred()
    S.ajax({'type': "POST", 'url': url, 'data': { },
        'success': lambda d: deferred.resolve(d),
        'error': lambda e: deferred.reject(e)
    })
    return deferred.promise()

使用异步代码,就像同步

async def readALot():
    try:
        result1 = await read("url_1")
        result2 = await read("url_2")
    except Exception:
        console.warn("Reading a lot failed")

A27:

The question was:

How do I return the response from an asynchronous call?

which CAN be interpreted as:

How to make asynchronous code look synchronous?

The solution will be to avoid callbacks, and use a combination of Promises and async/await.

I would like to give an example for a Ajax request.

(Although it can be written in Javascript, I prefer to write it in Python, and compile it to Javascript using Transcrypt. It will be clear enough.)

Lets first enable JQuery usage, to have $ available as S:

__pragma__ ('alias', 'S', '$')

Define a function which returns a Promise, in this case an Ajax call:

def read(url: str):
    deferred = S.Deferred()
    S.ajax({'type': "POST", 'url': url, 'data': { },
        'success': lambda d: deferred.resolve(d),
        'error': lambda e: deferred.reject(e)
    })
    return deferred.promise()

Use the asynchronous code as if it were synchronous:

async def readALot():
    try:
        result1 = await read("url_1")
        result2 = await read("url_2")
    except Exception:
        console.warn("Reading a lot failed")

回答28:

使用承诺

此问题的最完美答案是使用Promise

function ajax(method, url, params) {
  return new Promise(function(resolve, reject) {
    var xhr = new XMLHttpRequest();
    xhr.onload = function() {
      resolve(this.responseText);
    };
    xhr.onerror = reject;
    xhr.open(method, url);
    xhr.send(params);
  });
}

用法

ajax("GET", "/test", "acrive=1").then(function(result) {
    // Code depending on result
})
.catch(function() {
    // An error occurred
});

但是等等...!

使用诺言有问题!

为什么我们应该使用自己的自定义Promise?

我使用这种解决方案已有一段时间,直到发现旧浏览器中出现错误:

Uncaught ReferenceError: Promise is not defined

因此,我决定为 ES3以下 js编译器实现自己的Promise类(如果未定义)。只需在您的主要代码之前添加此代码,然后安全地使用Promise!

if(typeof Promise === "undefined"){
    function _classCallCheck(instance, Constructor) {
        if (!(instance instanceof Constructor)) { 
            throw new TypeError("Cannot call a class as a function"); 
        }
    }
    var Promise = function () {
        function Promise(main) {
            var _this = this;
            _classCallCheck(this, Promise);
            this.value = undefined;
            this.callbacks = [];
            var resolve = function resolve(resolveValue) {
                _this.value = resolveValue;
                _this.triggerCallbacks();
            };
            var reject = function reject(rejectValue) {
                _this.value = rejectValue;
                _this.triggerCallbacks();
            };
            main(resolve, reject);
        }
        Promise.prototype.then = function then(cb) {
            var _this2 = this;
            var next = new Promise(function (resolve) {
                _this2.callbacks.push(function (x) {
                    return resolve(cb(x));
                });
            });
            return next;
        };
        Promise.prototype.catch = function catch_(cb) {
            var _this2 = this;
            var next = new Promise(function (reject) {
                _this2.callbacks.push(function (x) {
                    return reject(cb(x));
                });
            });
            return next;
        };
        Promise.prototype.triggerCallbacks = function triggerCallbacks() {
            var _this3 = this;
            this.callbacks.forEach(function (cb) {
                cb(_this3.value);
            });
        };
        return Promise;
    }();
}

A28:

Using Promise

The most perfect answer to this question is using Promise.

function ajax(method, url, params) {
  return new Promise(function(resolve, reject) {
    var xhr = new XMLHttpRequest();
    xhr.onload = function() {
      resolve(this.responseText);
    };
    xhr.onerror = reject;
    xhr.open(method, url);
    xhr.send(params);
  });
}

Usage

ajax("GET", "/test", "acrive=1").then(function(result) {
    // Code depending on result
})
.catch(function() {
    // An error occurred
});

But wait...!

There is a problem with using promises!

Why should we use our own custom Promise?

I was using this solution for a while until I figured out there is an error in old browsers:

Uncaught ReferenceError: Promise is not defined

So i decided to implement my own Promise class for ES3 to below js compilers if its not defined. Just add this code before your main code and then safely use Promise!

if(typeof Promise === "undefined"){
    function _classCallCheck(instance, Constructor) {
        if (!(instance instanceof Constructor)) { 
            throw new TypeError("Cannot call a class as a function"); 
        }
    }
    var Promise = function () {
        function Promise(main) {
            var _this = this;
            _classCallCheck(this, Promise);
            this.value = undefined;
            this.callbacks = [];
            var resolve = function resolve(resolveValue) {
                _this.value = resolveValue;
                _this.triggerCallbacks();
            };
            var reject = function reject(rejectValue) {
                _this.value = rejectValue;
                _this.triggerCallbacks();
            };
            main(resolve, reject);
        }
        Promise.prototype.then = function then(cb) {
            var _this2 = this;
            var next = new Promise(function (resolve) {
                _this2.callbacks.push(function (x) {
                    return resolve(cb(x));
                });
            });
            return next;
        };
        Promise.prototype.catch = function catch_(cb) {
            var _this2 = this;
            var next = new Promise(function (reject) {
                _this2.callbacks.push(function (x) {
                    return reject(cb(x));
                });
            });
            return next;
        };
        Promise.prototype.triggerCallbacks = function triggerCallbacks() {
            var _this3 = this;
            this.callbacks.forEach(function (cb) {
                cb(_this3.value);
            });
        };
        return Promise;
    }();
}

回答29:

有2个概念是理解JS如何处理回调和异步性的关键,而不是向您扔代码。 (哪怕是一个字?)

事件循环和并发模型 < / strong>

您需要注意三件事: 队列; 事件循环和堆栈 < / p>

广义上来说,事件循环就像项目管理器一样,它一直在侦听任何想要运行的功能,并在队列和堆栈之间进行通信。

while (queue.waitForMessage()) {
   queue.processNextMessage();
}

一旦收到运行某条消息的消息,就会将其添加到队列中。队列是等待执行的事物的列表(例如您的AJAX请求)。像这样想象:

 1. call foo.com/api/bar using foobarFunc
 2. Go perform an infinite loop
 ... and so on

当其中一条消息要执行时,它将消息从队列中弹出并创建一个堆栈,堆栈是JS执行该消息中的指令所需执行的所有操作。因此,在我们的示例中,它被告知调用foobarFunc

function foobarFunc (var) {
  console.log(anotherFunction(var));
}

因此,foobarFunc需要执行的所有操作(在我们的示例中为anotherFunction)都将被压入堆栈。执行,然后被遗忘-事件循环将移至队列中的下一个事物(或侦听消息)

这里的关键是执行顺序。那是

何时要运行

当您使用AJAX呼叫外部方或运行任何异步代码(例如setTimeout)时,JavaScript依赖于响应才能继续。

最大的问题是,它将何时获得响应?答案是我们不知道-事件循环正在等待该消息说"嘿,我快跑"。如果JS只是同步地等待该消息,则您的应用程序将冻结,并且将变得很糟糕。因此,JS在等待消息添加回队列的同时继续执行队列中的下一项。

这就是为什么我们使用异步功能称为回调的原因。从字面上看,这有点像承诺。就像我在承诺在某个时候返回某些内容一样,jQuery使用特定的回调称为deffered.done deffered.faildeffered.always (以及其他)。您可以在此处

因此,您需要做的是传递一个函数,该函数应在传递给它的数据的某个点执行。

因为回调不会立即执行,但是在以后的某个时间将引用传递给未执行的函数很重要。所以

function foo(bla) {
  console.log(bla)
}

因此大多数情况下(但并非总是如此),您将传递foo而不是foo()

希望这会有所道理。当您遇到这样令人困惑的事情时,我强烈建议您完整阅读文档以至少了解它。它将使您成为更好的开发人员。

A29:

Rather than throwing code at you, there are 2 concepts that are key to understanding how JS handles callbacks and asynchronicity. (is that even a word?)

The Event Loop and Concurrency Model

There are three things you need to be aware of; The queue; the event loop and the stack

In broad, simplistic terms, the event loop is like the project manager, it is constantly listening for any functions that want to run and communicates between the queue and the stack.

while (queue.waitForMessage()) {
   queue.processNextMessage();
}

Once it receives a message to run something it adds it to the queue. The queue is the list of things that are waiting to execute (like your AJAX request). imagine it like this:

 1. call foo.com/api/bar using foobarFunc
 2. Go perform an infinite loop
 ... and so on

When one of these messages is going to execute it pops the message from the queue and creates a stack, the stack is everything JS needs to execute to perform the instruction in the message. So in our example it's being told to call foobarFunc

function foobarFunc (var) {
  console.log(anotherFunction(var));
}

So anything that foobarFunc needs to execute (in our case anotherFunction) will get pushed onto the stack. executed, and then forgotten about - the event loop will then move onto the next thing in the queue (or listen for messages)

The key thing here is the order of execution. That is

WHEN is something going to run

When you make a call using AJAX to an external party or run any asynchronous code (a setTimeout for example), Javascript is dependant upon a response before it can proceed.

The big question is when will it get the response? The answer is we don't know - so the event loop is waiting for that message to say "hey run me". If JS just waited around for that message synchronously your app would freeze and it will suck. So JS carries on executing the next item in the queue whilst waiting for the message to get added back to the queue.

That's why with asynchronous functionality we use things called callbacks. It's kinda like a promise quite literally. As in I promise to return something at some point jQuery uses specific callbacks called deffered.done deffered.fail and deffered.always (amongst others). You can see them all here

So what you need to do is pass a function that is promised to execute at some point with data that is passed to it.

Because a callback is not executed immediately but at a later time it's important to pass the reference to the function not it executed. so

function foo(bla) {
  console.log(bla)
}

so most of the time (but not always) you'll pass foo not foo()

Hopefully that will make some sense. When you encounter things like this that seem confusing - i highly recommend reading the documentation fully to at least get an understanding of it. It will make you a much better developer.

回答30:

使用ES2017,您应该将此作为函数声明

async function foo() {
    var response = await $.ajax({url: '...'})
    return response;
}

并像这样执行它。

(async function() {
    try {
        var result = await foo()
        console.log(result)
    } catch (e) {}
})()

或者Promise语法

foo().then(response => {
    console.log(response)

}).catch(error => {
    console.log(error)

})

A30:

Using ES2017 you should have this as the function declaration

async function foo() {
    var response = await $.ajax({url: '...'})
    return response;
}

And executing it like this.

(async function() {
    try {
        var result = await foo()
        console.log(result)
    } catch (e) {}
})()

Or the Promise syntax

foo().then(response => {
    console.log(response)

}).catch(error => {
    console.log(error)

})
回到顶部