Javascript 如何从异步调用返回响应?
我有一个函数Javascript 如何从异步调用返回响应?,javascript,jquery,ajax,asynchronous,Javascript,Jquery,Ajax,Asynchronous,我有一个函数foo,它发出异步请求。如何从foo返回响应/结果 我试图从回调中返回值,并将结果赋给函数中的局部变量,然后返回该局部变量,但这些方法实际上都不会返回响应(它们都返回undefined或变量result的初始值) 使用jQuery的ajax函数的示例: function foo() { var result; $.ajax({ url: '...', success: function(response) {
foo
,它发出异步请求。如何从foo
返回响应/结果
我试图从回调中返回值,并将结果赋给函数中的局部变量,然后返回该局部变量,但这些方法实际上都不会返回响应(它们都返回undefined
或变量result
的初始值)
使用jQuery的ajax
函数的示例:
function foo() {
var result;
$.ajax({
url: '...',
success: function(response) {
result = response;
// return response; // <- I tried that one as well
}
});
return result; // It always returns `undefined`
}
function foo() {
var result;
fs.readFile("path/to/file", function(err, data) {
result = data;
// return data; // <- I tried that one as well
});
return result; // It always returns `undefined`
}
function foo() {
var result;
fetch(url).then(function(response) {
result = response;
// return response; // <- I tried that one as well
});
return result; // It always returns `undefined`
}
function callback(response) {
// Here you can do what ever you want with the response object.
console.log(response);
}
$.ajax({
url: "...",
success: callback
});
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);
});
});
→ 有关使用不同示例的异步行为的更一般性解释,请参见
→ 如果您已经理解了问题,请跳到下面可能的解决方案
问题
中的A表示。这意味着发送请求(或者更确切地说接收响应)将从正常执行流中删除。在您的示例中,$.ajax
立即返回,下一条语句,返回结果在调用作为success
回调传递的函数之前执行code>
这里有一个类比,希望能使同步流和异步流之间的区别更清楚:
同步的
想象一下,你打电话给一个朋友,让他帮你查一些东西。虽然这可能需要一段时间,但你会在电话上等待并凝视太空,直到你的朋友给你你需要的答案
当您进行包含“正常”代码的函数调用时,也会发生同样的情况:
尽管执行findItem
可能需要很长时间,但在var item=findItem()之后出现的任何代码代码>必须等待函数返回结果
异步的
你再次打电话给你的朋友也是出于同样的原因。但这次你告诉他你赶时间,他应该用你的手机给你回电话。你挂断电话,离开家,做你计划做的任何事。一旦你的朋友给你回电话,你就是在处理他给你的信息
这正是执行Ajax请求时发生的情况
findItem(function(item) {
// Do something with the item
});
doSomethingElse();
不等待响应,而是立即继续执行,并在执行Ajax调用后执行语句。为了最终得到响应,您提供了一个在收到响应后调用的函数,即回调(注意到什么?回调?)。在该调用之后出现的任何语句都将在调用回调之前执行
解决方案
拥抱JavaScript的异步特性虽然某些异步操作提供了同步对应项(“Ajax”)也提供了同步对应项,但通常不鼓励使用它们,尤其是在浏览器上下文中
你问为什么不好
JavaScript在浏览器的UI线程中运行,任何长时间运行的进程都会锁定UI,使其无响应。此外,JavaScript的执行时间有一个上限,浏览器会询问用户是否继续执行
所有这些都会导致非常糟糕的用户体验。用户无法判断是否一切正常。此外,对于连接速度较慢的用户,效果会更差
在下文中,我们将介绍三种不同的解决方案,它们都是相互叠加的:
- 承诺使用
async/await
(ES2017+,如果您使用transpiler或regenerator,则可在较旧的浏览器中使用)
- 回调(在节点中流行)
- 承诺使用
then()
(ES2015+,如果您使用众多承诺库中的一个,则可在较旧的浏览器中使用)
在当前浏览器和node 7+中都可以使用这三种浏览器。
ES2017+:承诺与
2017年发布的ECMAScript版本引入了对异步函数的语法级别支持。在async
和await
的帮助下,您可以以“同步样式”编写异步。代码仍然是异步的,但更易于阅读/理解
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();
async/await
构建在承诺之上:一个async
函数总是返回一个承诺wait
“解除”承诺,要么导致承诺解析时使用的值,要么在承诺被拒绝时抛出错误
重要提示:只能在async
函数中使用wait
。目前,尚不支持顶级await
,因此您可能需要创建一个异步IIFE()来启动async
上下文
您可以阅读有关MDN的更多信息
下面是一个示例,详细介绍了上面的延迟函数findItem()
:
// Using 'superagent' which will return a promise.
var superagent = require('superagent')
// This is isn't declared as `async` because it already returns a promise
function delay() {
// `delay` returns a promise
return new Promise(function(resolve, reject) {
// Only `delay` is able to resolve or reject the promise
setTimeout(function() {
resolve(42); // After 3 seconds, resolve the promise with value 42
}, 3000);
});
}
async function getAllBooks() {
try {
// GET a list of book IDs of the current user
var bookIDs = await superagent.get('/user/books');
// wait for 3 seconds (just for the sake of this example)
await delay();
// GET information about each book
return 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);
})();
当前和版本支持异步/await
。您还可以在(或使用regenerator的工具,如)的帮助下将代码转换为ES5,从而支持较旧的环境
让函数接受回调
回调是指将函数1传递给函数2。函数2可以随时调用函数1。在异步进程的上下文中,只要异步进程完成,就会调用回调。通常,结果会传递给回调
function callServerAsync(){
$.ajax({
url: '...',
success: function(response) {
successCallback(response);
}
});
}
function successCallback(responseObj){
// Do something like read the response and show data
alert(JSON.stringify(responseObj)); // Only applicable to a JSON response
}
function foo(callback) {
$.ajax({
url: '...',
success: function(response) {
return callback(null, response);
}
});
}
var result = foo(function(err, result){
if (!err)
console.log(result);
});
在问题示例中,您可以使foo
接受回调并将其用作success
回调。那么这个
var result = foo();
// Code that depends on 'result'
变成
foo(function(result) {
// Code that depends on 'result'
});
这里我们定义了函数“inline”,但您可以传递任何函数引用:
function myCallback(result) {
// Code that depends on 'result'
}
foo(myCallback);
foo
本身的定义如下:
function foo(callback) {
$.ajax({
// ...
success: callback
});
}
order_milk() . then(put_in_coffee)
callback
将引用调用时传递给foo
的函数,并将其传递给success
。也就是说,一旦Ajax请求成功,$。Ajax
将调用回调
,并将响应传递给回调(可以使用结果
引用,因为这是我们定义回调的方式)
您还可以处理res
getFive(onComplete);
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);
}
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);
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();
}
function ajax(a, b, c){ // URL, callback, just a placeholder
c = new XMLHttpRequest;
c.open('GET', a);
c.onload = b;
c.send()
}
this.response
e.target.response
function callback(e){
console.log(this.response);
}
ajax('URL', callback);
ajax('URL', function(e){console.log(this.response)});
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
var fd = new FormData(form);
x(url, callback, 'post', fd);
var fd = new FormData();
fd.append('key', 'val')
x(url, callback, 'post', fd);
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);
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');
function callServerAsync(){
$.ajax({
url: '...',
success: function(response) {
successCallback(response);
}
});
}
function successCallback(responseObj){
// Do something like read the response and show data
alert(JSON.stringify(responseObj)); // Only applicable to a JSON response
}
function foo(callback) {
$.ajax({
url: '...',
success: function(response) {
return callback(null, response);
}
});
}
var result = foo(function(err, result){
if (!err)
console.log(result);
});
function handleData( responseData ) {
// Do what you want with the data
console.log(responseData);
}
$.ajax({
url: "hi.php",
...
success: function ( data, status, XHR ) {
handleData(data);
}
});
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.
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));
}
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.
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');
});
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
var milk = order_milk();
put_in_coffee(milk);
order_milk(put_in_coffee);
order_milk(function(milk) { put_in_coffee(milk, drink_coffee); }
var answer;
$.ajax('/foo.json') . done(function(response) {
callback(response.data);
});
function callback(data) {
console.log(data);
}
order_milk() . then(put_in_coffee)
order_milk() . then(put_in_coffee) . then(drink_coffee)
function get_data() {
return $.ajax('/foo.json');
}
get_data() . then(do_something)
get_data() .
then(function(data) { console.log(data); });
get_data() .
then(data => console.log(data));
a();
b();
a() . then(b);
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);
}
if (!name) {
name = async1();
}
async2(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
}
[
"search?type=playlist&q=%22doom%20metal%22",
"search?type=playlist&q=Adele"
]
-H "Authorization: Bearer {your access token}"
function callback(response) {
// Here you can do what ever you want with the response object.
console.log(response);
}
$.ajax({
url: "...",
success: callback
});
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)
});
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);
});
});
(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);
});
})();
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();
// WRONG
var results = [];
theArray.forEach(function(entry) {
doSomethingAsync(entry, function(result) {
results.push(result);
});
});
console.log(results); // E.g., using them, returning them, etc.
$(document).ready(function(){
function foo() {
$.ajax({url: "api/data", success: function(data){
fooDone(data); // After we have data, we pass it to fooDone
}});
};
function fooDone(data) {
console.log(data); // fooDone has the data and console.log it
};
foo(); // The call happens here
});
(async function(){
var response = await superagent.get('...')
console.log(response)
})()
function foo() {
var result;
$.ajax({
url: '...',
success: function(response) {
myCallback(response);
}
});
return result;
}
function myCallback(response) {
// Does something.
}
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
function foo(){
return new Promise((resolve, reject) => { // I want foo() to PROMISE me something
setTimeout ( function(){
// Promise is RESOLVED, when the 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'
});
function saveUsers(){
getUsers()
.then(users => {
saveSomewhere(users);
})
.catch(err => {
console.error(err);
})
}
async function saveUsers(){
try{
let users = await getUsers()
saveSomewhere(users);
}
catch(err){
console.error(err);
}
}
__pragma__ ('alias', 'S', '$')
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")
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");
}
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
const [err, data] = yield fs.readFile(filePath, "utf-8", callback);
while (queue.waitForMessage()) {
queue.processNextMessage();
}
function foobarFunc (var) {
console.log(anotherFunction(var));
}
function foo(bla) {
console.log(bla)
}
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
});
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;
}();
}
const body = document.getElementsByTagName('body')[0];
function callback() {
console.log('Hello');
}
body.addEventListener('click', callback);
asyncCallOne(function callback1() {
asyncCallTwo(function callback2() {
asyncCallThree(function callback3() {
...
})
})
})
const myFirstPromise = new Promise((resolve, reject) => {
// We call resolve(...) when what we were doing asynchronously was successful, and reject(...) when it failed.
// In this example, we use setTimeout(...) to simulate async code.
// In reality, you will probably be using something like XHR request or an HTML5 API.
setTimeout(() => {
resolve("Success!") // Yay! Everything went well!
}, 250)
})
myFirstPromise
.then((res) => {
return res.json();
})
.then((data) => {
console.log(data);
})
.catch((e) => {
console.log(e);
});
then — Runs a callback you pass to it when the promise has fulfilled.
catch — Runs a callback you pass to it when something went wrong.
const getExchangeRate = async () => {
try {
const res = await fetch('https://getExchangeRateData');
const data = await res.json();
console.log(data);
} catch (err) {
console.error(err);
}
}
getExchangeRate();