Is it possible, somehow, to call async function (such as timer / ajax
call), basically common async task and synchronously wait until it
ends without having 100%CPU usage and blocked browser?
No. You can't "synchronously wait" for an async result in Javascript. Because of it's single threaded and event-driven design, it just won't work that way.
As the majority of the other talk/comments about this question have been conceptual in nature, I thought I'd offer an answer about how you would practically solve your specific problem using an appropriate asynchronous design.
Javascript does not offer the ability to sleep while some time consuming activity finishes. So, you cannot design your updateCSS()
function to have a purely synchronous interface. Instead, you will need to use asynchronous tools in Javascript to design an interface that would work for both the case where the resource is cached and when the resource must be fetched over the network.
The usual way to design for that is to design an async interface and, if the resource happens to be cached, you still serve it through the async interface. Then, you have one interface that the caller can use and that interface always works whether the resource is cached or not. It requires the caller to use an async interface.
Practically, you can design like this 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(...);
}
}
Here getCachedResource()
always returns a promise, whether the value is cached or not. So, the caller always uses the promise interface like this:
getCachedResource(...).then(function(result) {
// use the result here
});
If the result happens to be cached, the .then()
handler will be called immediately (on the next tick). If the result is retrieved over the network, the .then()
handler will be called as soon as the result is available. The caller doesn't need to worry about where or how the resource was retrieved - they have the one interface for getting it.
In the updateCSS()
function, you're going to have to design for using the asynchronous interface in getCachedResource()
. Because your current design uses a synchronous callback in css.replace()
, you'll have to rethink that a bit to fit the async retrieval into there. Here's one approach for how to do that.
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;
})
});
}
The scheme here is to run a dummy .replace()
that does not keep the result to generate the list of urls you need to load. Depending upon your regex, this could also be done with .match()
or .exec()
. Then, once you have the list of URLs to fetch, go get them all. When you have all of them, then you can run the .replace()
for real because you have the resources available to use in the synchronous .replace()
callback.
One could then use this like this:
updateCss(someCSSURL).then(function(css) {
// use the CSS here
}).catch(function(err) {
// process error here
});