我如何从函数 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`
}
而不是把代码扔在你身上,有两个概念是了解JavaScript如何处理呼叫反馈和无同步性(甚至是一个词?)的关键。
活动流程和汇率模型
你需要知道的三件事:尾巴、事件圈和尾巴。
while (queue.waitForMessage()) {
queue.processNextMessage();
}
一旦收到一个消息运行某件事,它将其添加到尾巴上,尾巴是等待执行的事情的列表(如您的AJAX请求)。
当其中一个消息将执行时,它将从字符串中打开消息并创建一个字符串,字符串是所有JavaScript需要执行以执行消息中的指示。
function foobarFunc (var) {
console.log(anotherFunction(var));
}
因此,任何 foobarFunc 需要执行的东西(在我们的情况下,另一个功能)将被推到架子上。执行,然后被遗忘 - 事件圈将随后移动到接下来的东西在架子上(或听到消息)
这里的关键是执行命令。
什么时候会发生什么事
function foo(bla) {
console.log(bla)
}
另一个解决方案是通过序列执行器 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 将逐步评估所有运营商和表达式,如果某些缓慢功能的结果不准备好,则停止执行。
这里有更多的例子。
您可以使用此自定义图书馆(使用 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)
});
下面我写的例子表明如何
处理无同步的 HTTP 通话; 等待每个 API 通话的响应; 使用 Promise 模式; 使用 Promise.all 模式加入多个 HTTP 通话;
[
"search?type=playlist&q=%22doom%20metal%22",
"search?type=playlist&q=Adele"
]
对于每个项目,一个新的承诺将燃烧一个区块 - ExecutionBlock,打破结果,根据结果序列安排一个新的承诺集,这是 Spotify 用户对象的列表,并在 ExecutionProfileBlock 中无同步执行新的 HTTP 通话。
然后,你可以看到一个被遗弃的承诺结构,允许你扫描多个和完全无同步的遗弃的HTTP通话,并通过 Promise.all 加入每个子组的通话的结果。
-H "Authorization: Bearer {your access token}"
我在这里讨论了这个解决方案。
起初,呼叫反馈被用于无同步操作(例如,在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的辅助功能。
虽然承诺和呼叫在许多情况下工作顺利,但背部疼痛是表达这样的东西:
if (!name) {
name = async1();
}
async2(name);
您最终会通过 async1; 检查名称是否不定义,并根据此呼叫回复。
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
}
您可以在此处查看项目。