我如何从函数 foo 返回一个无同步请求的答案/结果?
我正在尝试从呼叫返回的值,以及将结果分配到函数内部的本地变量,并返回其中一个,但没有这些方式实际上返回答案 - 他们都返回不确定的或无论变量结果的初始值是什么。
一个不同步函数的例子,接受召回(使用 jQuery 的 ajax 函数):
function foo() {
var result;
$.ajax({
url: '...',
success: function(response) {
result = response;
// return response; // <- I tried that one as well
}
});
return result; // It always returns `undefined`
}
使用 Node.js 的例子:
function foo() {
var result;
fs.readFile("path/to/file", function(err, data) {
result = data;
// return data; // <- I tried that one as well
});
return result; // It always returns `undefined`
}
例如,使用那时承诺的区块:
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`
}
问题是:
我如何从无同步通话中返回答案?
可以被解释为:
如何让同步代码看起来同步?
解决方案将是避免呼叫,并使用一个组合的承诺和async/await。
我想举一个例子给一个Ajax请求。
(虽然它可以用JavaScript写作,但我宁愿用Python写作,并用Transcrypt编写到JavaScript。
首先,让我们启用 jQuery 使用,以便有 $ 作为 S 可用:
__pragma__ ('alias', 'S', '$')
设置一个函数返回承诺,在这种情况下,一个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")
起初,呼叫反馈被用于无同步操作(例如,在XMLHttpRequest API)。现在,像浏览器的Fetch API这样的基于承诺的APIs已成为默认解决方案,并且所有现代浏览器和Node.js(服务器侧)都支持更轻松的async/await合成。
一个常见的场景 - 从服务器中获取 JSON 数据 - 可以看起来如下:
async function fetchResource(url) {
const res = await fetch(url);
if (!res.ok) {
throw new Error(res.statusText);
}
return res.json();
}
在另一个功能中使用:
async function doSomething() {
try {
const data = await fetchResource("https://example.test/resource/1");
// ...
} catch (e) {
// Handle error
...
}
}
如果你设计一个现代的API,强烈建议你更喜欢基于承诺的风格,而不是呼叫回复,如果你继承了一个基于呼叫回复的API,你可以把它作为承诺:
function sleep(timeout) {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, timeout);
});
}
async function fetchAfterTwoSeconds(url) {
await sleep(2000);
return fetchResource(url);
}
在Node.js中,历史上仅依赖于呼叫反馈,这种技术是如此常见,以至于它们添加了一个名为util.promisify的辅助功能。
如果您不使用 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'
(注意,对于使用新的Fetch API、Angular 或承诺的人,我在下面添加了另一个答案)
你所面临的
这里有一个简单的模拟:
function getFive(){
var a;
setTimeout(function(){
a=5;
},10);
return a;
}
(重定向)
function onComplete(a){ // When the code completes, do this
alert(a);
}
function getFive(whenDone){
var a;
setTimeout(function(){
a=5;
whenDone(a);
},10);
}
使用将是:
getFive(onComplete);
同步AJAX - 不要这样做!!!
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();
}
我们现在已经使我们的 foo 函数接受一个行动运行,当 AJAX 成功完成时,我们可以通过检查答案状态是否不为 200 进行进一步扩展,并按照此进行操作(创建一个错误处理器等)。
如果你仍然很难理解这一点,请阅读MDN的AJAX开始指南。