我知道这里有很多次被问过,很多时候回答这不是应该怎样做的方式,而是再一次:)
是否有可能以某种方式调用异步函数(例如timer / ajax调用),基本上是常见的异步任务并同步等到它结束而没有100%的CPU使用率并阻止浏览器?
简单的回答就足够了 - 是或否。如果不是我必须根据“异步方式”中的异步操作编写所有代码,否则,它会更好;)
想象一下:
updateCSS("someurl.css")
function updateCSS(url) {
var css = getCachedResource(url);
css = css.replace(/regexp/gm, function(curUrl) {
base64 = atob(getCachedResource(curUrl))
return "data:image;base64," + base64
})
}
function getCachedResource(url) {
//...
if (cached) {
return cached
} else {
// doajax...
// watfor
return loaded
}
}
当然,我可以让它异步,但代码会......非常糟糕。而且,我必须使调用此函数的所有函数都异步。你能看到简单的解决方案吗我会说阻塞是最简单的。
由于
答案 0 :(得分:1)
是
您希望代码“休眠”,直到“异步功能(例如定时器/ ajax调用)...结束而没有100%的CPU使用率”。这正是async
/await
所做的。
其他答案和评论都强调你的短语“同步等待”,这在JS中意味着阻止,但我理解你只是想避免必须重构你的代码“异步方式”。你想直截了当地写,而不是将所有内容都构造成无数的回调。
要使用async
/ await
,首先必须接受承诺 - 异步JavaScript的空间/时间。然后,您需要一个像Chrome或Firefox Developer Edition这样的现代浏览器,或者使用Babel js。
接下来,你想看看fetch
,这是承诺回归的现代方式来做ajax。
现在你拥有了所需的一切。这是一个适用于上述浏览器的示例:
async function fetchy(...args) {
let response = await fetch(...args);
if (!response.ok) throw new Error((await response.json()).error_message);
return response;
}
async function showUser(user) {
let url = `https://api.stackexchange.com/2.2/users/${user}?site=stackoverflow`;
let item = (await (await fetchy(url)).json()).items[0];
console.log(JSON.stringify(item, null, 2));
let blob = await (await fetchy(item.profile_image)).blob();
img.src = URL.createObjectURL(blob);
}
input.oninput = async e => await showUser(e.target.value);
showUser(input.value);
<input id="input" type="number" value="918910"></input><br>
<img id="img"></img>
<pre id="pre"></pre>
仅供参考,fetchy是理解fetch
错误处理的助手。
答案 1 :(得分:1)
否,即使使用100%的cpu或阻止的浏览器,也无法做到这一点。事情已经精确设计,以防止你这样做。
这使得所有异步代码都有毒,这意味着调用异步函数(基于promise或回调)的所有代码本身必须是异步的。
关于可怕的代码:Promise
链和async/await
用于救援,它们可以帮助您编写具有更好语法的异步代码。 不要害怕,请使用async/await
,尤其是如果您已经使用过转发器。
答案 2 :(得分:0)
正如其他人所指出的那样,简短的答案是否定的:Javascript在大多数使用它的环境中都是单线程的。你要做的是在getCachedResource()的另一个回调中处理你的CSS
这样的事情:
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">>
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="toolbarStyle">@style/AppTheme.Toolbar</item>
</style>
看看jQuery“延迟”对象或Javascript承诺,以更完整的方式处理回调
答案 3 :(得分:0)
所以只是总结......
答案是否定的。永远不可能阻止该函数或任何其他代码,并等待异步操作完成。 JavaScript VM没有直接支持它的机制。
特别是在浏览器环境中,无法操作JavaScript虚拟机事件循环(因为可以使用Node以及实际上&#34; deasync&#34;库正在执行此操作)。
这是因为这里提到的JavaScript并发模型和事件处理:
https://developer.mozilla.org/cs/docs/Web/JavaScript/EventLoop
它与JavaScript是单线程无关。
另外,请注意,像Promise这样的东西和await / async等新功能只是一种不同的方式,如何编写/管理异步任务的代码。它不是也不会有助于将异步转为同步。
所以不要像我一样花时间试图找到一种方法来做到这一点。而是通过设计异步代码来花费它;)
答案 4 :(得分:0)
简单地说,答案是否定的。
不要这么想。考虑后端性能,而不是在客户端做。
答案 5 :(得分:0)
是否有可能以某种方式调用异步函数(例如timer / ajax) 调用),基本上是常见的异步任务并同步等到它 没有100%的CPU使用率和阻止浏览器结束?
没有。您无法在Javascript中“同步等待”异步结果。由于它是单线程和事件驱动的设计,它不会那样工作。
由于关于这个问题的大多数其他谈话/评论都是概念性的,我想我会提供一个答案,说明如何使用适当的异步设计实际解决您的具体问题。
Javascript不提供睡眠能力,而一些耗时的活动结束。因此,您无法将updateCSS()
函数设计为具有纯粹的同步接口。相反,您需要在Javascript中使用异步工具来设计一个接口,该接口既适用于缓存资源的情况,也适用于必须通过网络获取资源的情况。
设计的常用方法是设计异步接口,如果资源恰好被缓存,您仍然可以通过异步接口提供服务。然后,您有一个调用者可以使用的接口,无论资源是否被缓存,该接口始终有效。它要求调用者使用异步接口。
实际上,您可以像getCachedResource()
一样进行设计:
function getCachedResource(url) {
//...
if (cached) {
// returns a promise who's resolved value is the resource
return Promise.resolve(cached);
} else {
// returns a promise who's resolved value is the resource
return doAjax(...);
}
}
此处getCachedResource()
始终返回一个承诺,无论该值是否被缓存。因此,调用者总是使用promise接口,如下所示:
getCachedResource(...).then(function(result) {
// use the result here
});
如果结果恰好被缓存,则会立即调用.then()
处理程序(在下一个tick中)。如果通过网络检索结果,则只要结果可用,就会调用.then()
处理程序。调用者不需要担心资源的检索位置和方式 - 他们有一个接口来获取资源。
在updateCSS()
函数中,您将不得不设计在getCachedResource()
中使用异步接口。因为您当前的设计在css.replace()
中使用同步回调,所以您必须重新考虑这一点以使异步检索适应那里。这是如何做到这一点的一种方法。
function updateCSS(url) {
return getCachedResource(url).then(function(css) {
// preflight the replacements we need to do so we know what to fetch
var urls = [];
css.replace(/regexp/gm, function(curUrl) {
urls.push(curUrl);
});
// now load all the resources
return Promise.all(urls.map(getCachedResource)).then(function(resources) {
// now have all the resources, in proper order
var index = 0;
css = css.replace(/regexp/gm, function(curUrl) {
return resources[index++];
});
// final resolved value is the fully loaded css
return css;
})
});
}
此处的方案是运行一个虚拟.replace()
,它不会保留结果以生成您需要加载的网址列表。根据您的正则表达式,也可以使用.match()
或.exec()
来完成此操作。然后,一旦你有了要获取的URL列表,就去获取它们。如果你拥有所有这些,那么你可以运行.replace()
for real,因为你有可用于同步.replace()
回调的资源。
然后可以这样使用:
updateCss(someCSSURL).then(function(css) {
// use the CSS here
}).catch(function(err) {
// process error here
});
答案 6 :(得分:0)
我喜欢所有其他答案,我理解为什么您不能使异步任务同步。我被带到这里是想弄清楚如何 await
让 FileReader 对象完成读取,以便我可以使用同一个 FileReader 对象读取多个文件。
我克服的个人问题:FileReader 对象不返回承诺,因此您不能等待。
我创建了一个递归函数,该函数将在循环中运行以执行第一次读取,并检查 .loadend
事件何时命中,然后移至下一个文件。对于那些批评者来说,是的,这可能是不好的做法,但确实有效。
回到您的问题,ajax 请求是异步的,就像我的示例一样,它不返回承诺,因此您不能使用等待。经过一番挖掘,我发现了一个 stackoverflow 帖子,它可能以实用的方式解决我们的两个问题:
How to await the ajax request?
您可以创建一个函数来获取 url 并返回 ajax 调用。这可以被 await
ed,因此您可以使用 .then(doSomethingAfter())
和所有 Promise 可以做的很酷的事情。试用后,我会告诉您 FileReader 的工作原理。
~~~~编辑~~~~
这是我的代码:
function getFileResolve(reader, files, currentNum) {
try {
var file = files[currentNum];
return reader.readAsText(file)
} catch(err) {
console.log(err)
}
};
function main() {
$('#imported-files').on('change', function() {
var files = this.files,
reader = new FileReader(),
currentFileNum = 0;
reader.onload = function(e) {
console.log(e.target.result);
currentFileNum++;
getFileResolve(reader, files, currentFileNum);
}
getFileResolve(reader, files, currentFileNum);
})
};
$(document).ready(main);
ajax 调用可以被返回,并且从那里可以被视为一个承诺。 FileReader 对象有一个 onloaded 事件,如前所述,因此可以为您调用下一个文件,并且它自己是递归的。希望这可以帮助那里的任何人。 #promises_are_hard