先看这样一个例子:
function foo() {
var result;
$.ajax({
url: ‘...‘,
success: function(response) {
result = response;
}
});
return result;
}
var result = foo(); // It always ends up being `undefined`.
在这个例子中,使用ajax进行异步请求,但是响应的返回值始终是undefined。那这是什么原因呢?这个问题最初也让我很困惑。下面我们来分析一下原因。
找出问题的原因
我们知道ajax中的a代表异步,那就意味着发送请求(亦或是接受响应)需要花费一点时间。而在上述例子中,$.ajax
是被立即返回的,return result;
这句语句在success
函数返回响应前就已经被执行了。主要原因还是没有理解异步和同步之间的区别。
下面这个类比希望可以使同步和异步的区别更清晰。
Synchronous(同步流)
想象你给你的一个朋友打电话,你希望他可以帮你一个忙,你会进行一段时间的等待,直到他的回答。
那么在正常同步流的代码中也会发生一样的事情。
function findItem() {
var item;
while(item_not_found) {
// search
}
return item;
}
var item = findItem();
// Do something with item
doSomethingElse();
执行 findItem
需要花费一段时间,所有在 var item = findItem();
这条语句之后的代码都需要等待函数返回结果。
Asynchronous(异步流)
同样你又给你的朋友打了一个电话,但是现在你很忙,你给你的朋友留言让他给你回一个电话,然后你挂掉电话就做其他的事情了,直到你的朋友回你电话,你才会停下手中的事情处理这个电话。
这也正是ajax请求的过程。
findItem(function(item) {
// Do something with item
});
doSomethingElse();
不去等待响应而是立即继续执行ajax之后的语句。而为了得到响应我们会定义一个回调函数,一旦收到响应就会执行这个回调函数。但是注意在ajax请求之后的语句会在回调函数调用前被执行。
解决问题的方法
1. 使用回调函数
上面的例子中
var result = foo();
// Code that depends on ‘result‘
改为
foo(function(result) {
// Code that depends on ‘result‘
});
然后我们给 foo
传递函数参数
function myCallback(result) {
// Code that depends on ‘result‘
}
foo(myCallback);
函数 foo
定义如下
function foo(callback) {
$.ajax({
// ...
success: callback
});
}
一旦ajax
请求成功,$.ajax
就会调用callback
函数并且将响应传递给callback
函数。
2.使用promise
Promise API是ES6新特性之一,但是各大浏览器已经有了很好的支持(IE11及以上支持)。MDN这么定义它:
Promise 对象用于异步计算。一个 Promise 表示一个现在、将来或永不可能可用的值
promise的语法:
new Promise(
/* executor */
function(resolve, reject) {...}
);
这里是一段使用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).
});
结合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
的更多信息。
3.jQuey: 使用延迟函数
jQery的延迟函数和promise
很像,但是API略有不同。每一个jQery的ajax方法都会返回一个延迟函数。
function ajax() {
return $.ajax(...);
}
ajax().done(function(result) {
// Code depending on result
}).fail(function() {
// An error occurred
});
始终要牢记promise
和延迟函数都只有一个存有未来值 的容器而不是值本身。看下面的例子:
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
的请求,jQery内部机制会立即会返回一个ajax延迟函数,而这个并不是服务器发回来的响应。这也就意味着下面的判断语句(if)始终都是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()
也会立即返回一个延迟对象,但是我们通过监听.done()
和.fail()
事件能正确处理服务器返回的响应。.done()
事件被调用时,服务器返回的是一个正常的响应(http 200),我们通过检查他的返回对象是否为true判断用户是否登陆。
.fail()
处理函数是处理一些错误的事件。例如用户网络断开或服务器错误等。
本文代码均来自StackOverflow上这个话题。
原文地址:https://www.cnblogs.com/syqcoding-life/p/9821762.html