我如何从函数 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`
}
另一种方法是从无同步函数中返回值,是通过将结果从无同步函数中存储的对象。
下面是相同的例子:
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');
});
我正在使用结果对象来存储在无同步操作期间的值,这使得结果即使在无同步工作后也可用。
我使用这个方法很多,我会感兴趣知道这个方法有多好运作,在那里通过连续模块将结果传输回来。
另一个解决方案是通过序列执行器 nsynjs 执行代码。
nsynjs 将连续评估所有承诺,并将承诺结果列入数据属性:
函数同步Code() { var getURL = 函数(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('收到的比特:',getURL(url).length); }; nsynjs.run(同步Code,{},函数(){ console.log('同步Code done'); }; <script src="https://rawgit.com/amaksr/
如果基本功能不承诺
步骤 1 将函数与呼叫回归到 nsynjs-aware 插槽中(如果它有一个有前途的版本,你可以错过这个步骤):
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;
function process() {
console.log('got data:', ajaxGet(nsynjsCtx, "data/file1.json").data);
}
步骤3:通过 nsynjs 以同步方式运行函数:
nsynjs.run(process,this,function () {
console.log("synchronous function finished");
});
Nsynjs 将逐步评估所有运营商和表达式,如果某些缓慢功能的结果不准备好,则停止执行。
这里有更多的例子。
选项 #1 - 从 foo 方法直接返回 Ajax 通话. 在 jQuery 的最新版本中,一个 Ajax 通话返回一个承诺的对象,可以使用.then 函数解决。
// 宣告函数 foo 函数 foo(url) {返回 $.get(url); } // 召回 foo 函数,返回一个承诺的对象 // 函数 '然后' 接受召回解决函数 foo('https://jsonplaceholder.typicode.com/todos/1').then(函数(回复) { console.log(回复); } ).as-console-wrapper { max-height: 100%!important;
选项 #2 - 宣布承诺对象并返回它. 在函数中宣布承诺对象,在承诺函数中嵌入 Ajax 呼叫,并返回承诺对象。
起初,呼叫反馈被用于无同步操作(例如,在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的辅助功能。
问题是:
我如何从无同步通话中返回答案?
可以被解释为:
如何让同步代码看起来同步?
解决方案将是避免呼叫,并使用一个组合的承诺和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")
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。
呼叫回归器被包围在一个序列,并在你阅读它们时被破坏,以便模式可以处理有多个论点的呼叫回归器。
const [err, data] = yield fs.readFile(filePath, "utf-8", callback);