I want to turn an asynchronous function to the synchronous.
function fetch() {
var result = 'snap!';
$.getJSON("http://api.flickr.com/services/feeds/photos_public.gne?tags=cat&tagmode=any&format=json&jsoncallback=?", function messyCallback(data){
result = data;
});
return result;
}
document.write(fetch());
See in action
The result always will be 'snap!', because $.getJSON run after fetch() is done.
My first idea was:
function fetch() {
var result = 'snap!';
$.getJSON("http://api.flickr.com/services/feeds/photos_public.gne?tags=cat&tagmode=any&format=json&jsoncallback=?", function messyCallback(data){
result = data;
});
while (true) {
if (result != 'snap!') return result;
}
}
It doesn't work and also blow off the browser.
I read about generators and iterators in JS 1.7, but I have no idea how to apply it to my problem.
This question is not really about jQuery. Instead of $.getJSON could be any another asynchronous function.
See this question also: Halt JavaScript execution without locking up the browser
Doing exactly what you want doesn't work. You can't create synchronousness from asynchronousness (only the other way around!) in a single-threaded event-driven environment. You should embrace the async nature of the language, and develop your own consistent style around handling callbacks so that your code is readable/maintainable. You could, for instance, choose to never do any real work inside a callback closure, but simply call another top-level function/method for clarity.
You want to roll your own $.getSyncJSON that uses $.ajax({async:false}). See: http://api.jquery.com/jQuery.ajax/.
I must advise you against this course of action, however. You can easily lock up the browser from all input, putting your UI at the mercy of the network. But if you know what you are doing and are sure of your use case, go for it.
Instead of writing helper methods like your fetch that return a value, make them accept another function, a "receiver", to pass their result to:
function fetch(receiver) {
$.getJSON("blah...", function(data) {
receiver(data);
});
}
Obviously this is redundant because it's exactly how getJSON already works, but in a more realistic example the fetch function would process or filter the result somehow before passing it on.
Then instead of:
document.write(fetch());
You'd do:
fetch(function(result) { document.write(result); });
Generators can be used to make asynchronous code a lot more linear in style. Each time you needed some asynchronous result, you'd yield a function to launch it, and the generator would resume when the result was available. There'd be a bit of management code keeping the generator going. But this isn't much help because generators are not standard across browsers.
If you're interested, here's a blog post about using generators to tidy up asynchronous code.
There is an extension to the JavaScript language called StratifiedJS. It runs in every browser, and it allows you to do just that: handling asynchronous problems in a synchronous/linear way without freezing your browser.
You can enable Stratified JavaScript e.g. by including Oni Apollo in your webpage like:
<script src="http://code.onilabs.com/latest/oni-apollo.js"></script>
<script type="text/sjs"> your StratifiedJS code here </script>
And your code would look like:
function fetch() {
return require("http").jsonp(
"http://api.flickr.com/services/feeds/photos_public.gne?" +
"tags=cat&tagmode=any&format=json", {cbfield:"jsoncallback"});
}
document.write(fetch());
Or if you really want to use jQuery in StratifiedJS:
require("jquery-binding").install();
function fetch() {
var url = "http://api.flickr.com/?format=json&...&jsoncallback=?"
return $.$getJSON(url);
}
document.write(fetch());
The docs are on http://onilabs.com/docs
The TameJS library is designed to deal with this problem.
You might write something like (untested):
var result = 'snap!';
await {
$.getJSON("http://api.flickr.com/services/feeds/photos_public.gne?tags=cat&tagmode=any&format=json&jsoncallback=?", defer(result));
}
return result;
The lower-level $.ajax() function from jQuery has more options, including async: [true|false] (default is true).
Nevertheless, in most cases you should follow Ben's advice and "embrace the async nature of the language".
I know this a little late but you can avoid callbacks with promises and await in async function
deliverResult = (options) => (
new Promise( (resolve, reject) => {
$.ajax(options).done(resolve).fail(reject);
})
)
getResult = async () => {
let options = {
type: 'get',
url: 'http://yourUrl.com',
data: {param1: 'arg1'}
}
console.log('waiting ..... ');
let result = await deliverResult(options);
console.log('**..waiting ended..**');
console.log(result);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input type='button' onclick='getResult()' value='click me'/>
Related
I've working on a Chrome extension which is going to collect information from the Chrome.System apis and make use of all that data at once. The problem of course is that these calls are asynchrounous. I've not got a ton of experience with JS, so I want to make sure I'm doing this the easiest way possible.
The only way I can think of is making nested functions in the callbacks.
Something Like:
chrome.identity.getProfileUserInfo(function(userinfo){
getLocalIPs(userinfo.email, function(email, ips){
//keep passing data and nesting here.... and then do something after all calls are made
}
}
This seems like it will get very hard to read the code very quickly. What's the recommened method to do something like this. With synchronous programming, I would want to accomplish something like this:
var email = getEmail();
var ip = getIP();
var processor = getProcessor();
dosomething(email, ip, processor);
Why reinvent the wheel when JavaScript already natively has tools to deal with it?
Specifically, Promises. There's a very good article at HTML5Rock introducing the concept.
You make functions that return promises:
function getEmailPromise() {
return new Promise(function(resolve, reject) {
chrome.identity.getProfileUserInfo(function(userinfo) {
resolve(userinfo.email);
});
});
}
And then you can do exactly what you want:
var email = getEmailPromise();
var ip = getIPPromise();
var processor = getProcessorPromise();
Promise.all([email, ip, processor]).then(function(data) {
// data is [email, ip, processor]
});
Update:
It's 2019 and Promises are well supported in browsers. So see Xan's answer
Original Answer:
The nested functions become a burden a few levels in. I would suggest running each asynchronous call in order, and setting a flag for each callback on completion.
I would then create a function runIfReady() which checks if all required flags are set before launching doSomething(). You can add runIfReady() to the end of every asynchronous callback. Once all of the details are in, the doSomething() will run immediately.
You do not have to worry about launching doSomething() multiple times, because although the callbacks are asynchronous, only one will run at a time.
I'm writing some node.js to interact with sensors over a serial port connection. The code for reading the sensor is asynchronous, naturally. In my control code, though, I need to read a sensor, do something based on the value, read again, do something else, etc. To do this, I'm using code like the following self-contained test:
var main = new Main();
main.next();
function* Main()
{
var reading = yield readSensor(this.next.bind(this));
console.log(reading);
var reading = yield readSensor(this.next.bind(this));
console.log(reading);
}
function readSensor(callback)
{
// simulate asynchrounous callback from reading sensor
setTimeout(function sensorCallback() { callback('foo'); }, 100);
}
So, my sequential control code is in a generator which yields to readSensor() when it needs to get a reading. When the sensor reading is done, it calls the callback, and control returns to the main code. I'm doing it this way because I may need to read from various sensors in different orders depending on previous readings. So, here's the questionable part: I pass this.next.bind(this) as a callback to the asynchronous read function. The code seems to work when generators are enabled (--harmony_generators), but I am wondering if there are pitfalls here that I am missing. I'm relatively new to JS, so don't be afraid to point out the obvious :)
I haven't studied ES6 generators in depth, but having a generator pass its own .next to another function as a callback doesn't sit well with me. If anything, it could create a situation where readSensor fails and you have no way to handle the failure, ending up in a deadlock.
I suggest modifying or wrapping readSensor to return a promise, and then using the technique outlined in this article.
That would allow you to write code like this (verified working in Node v0.12.0):
var Promise = require('q');
var main = async(function* () {
var reading = yield readSensor();
console.log(reading);
reading = yield readSensor();
console.log(reading);
});
main();
function readSensor() {
return Promise.delay(2000).thenResolve(Math.random() * 100);
}
/***********************************************************
* From here down, *
* boilerplate async() function from article linked above *
***********************************************************/
function async(makeGenerator){
return function () {
var generator = makeGenerator.apply(this, arguments);
function handle(result){
// result => { done: [Boolean], value: [Object] }
if (result.done) return Promise.resolve(result.value);
return Promise.resolve(result.value).then(function (res){
return handle(generator.next(res));
}, function (err){
return handle(generator.throw(err));
});
}
try {
return handle(generator.next());
} catch (ex) {
return Promise.reject(ex);
}
}
}
As loganfsmyth notes below, Q already provides a Q.async() method that provides the functionality of this async() function, and possibly other promise libraries do as well.
So, here's the questionable part: I pass this.next.bind(this) as a callback to the asynchronous read function. The code seems to work when generators are enabled
No, that doesn't work. Generators cannot be constructed using new like you do. From the spec:
If the generator was invoked using [[Call]], the this binding will have already been initialized in the normal manner. If the generator was invoked using [[Construct]], the this bind is not initialized and any references to this within the FunctionBody will produce a ReferenceError exception.
Generator functions be invoked with new (see §9.2.3, with derived for [[ConstructorKind]]), but they do not construct an instance.
When the sensor reading is done, […] control returns to the main code.
That's a clever idea indeed. It has been explored before, see Understanding code flow with yield/generators or this article. Many libraries support this, especially combined with promises.
I suggest you use one of these libraries, your current code isn't really stable (will break with full ES6 support) and also seems to lacks error handling.
I have a strict JavaScript API naming scheme I need to follow, it looks like this:
var Items = function() {
this.items = [];
};
Items.prototype.get() {
db.items.find(function(err, items) {
this.items = items;
});
return this.items;
}
The problem is the async call (db.items.find..) that doesn't have time to finish before the get() method returns an empty this.items..
The client needs to make the calls like this:
items = new Items();
console.log(items.get());
What's best practice to handle async calls here while still strictly following the API naming scheme?
Is there some native way I can let get() wait for a return inside the callback or do I need some kind of async lib for this?
EDIT:
Apparently what you are looking for might be possible using wait.for (https://github.com/luciotato/waitfor). I have not used it though, so I am not sure how well it would suit your needs. The method you would need would be wait.forMethod.
Previous Answer:
There is no way you can write async code in a synchronous manner. Also, it is not a good idea to mix async and sync methods. You are trying to define a synchronous method Item.prototype.get, but inside that you are using an async method db.items.find, which makes Item.prototype.get an asynchronous function. In order to get this to work you will have to define Item.prototype.get as a proper async function with a callback.
Items.prototype.get(fn) {
db.items.find(function(err, items) {
return fn(err, items);
});
}
You could then call it as
items = new Items();
items.get(function(err, items){
console.log(items);
}
I managed to solve this by using SilkJS (http://www.silkjs.net/), which is similar to Node in that it is built atop of V8 but it runs in synchronous mode.
This way I managed to keep the given API spec.
I'm writing tests for my Node.js/Express/Mongoose project using Mocha and Should.js, and I'm testing out my functions that access my MongoDB. I'm want these tests to be completely independent from the actual records in my database, so I want to create an entry and then load it, and do all my tests on it, then delete it. I have my actual functions written (I'm writing tests after the entire project is complete) such that the create function does not have a callback; it simply just renders a page when it's done. In my tests script, I call my load_entry function after I call create, but sometimes create takes longer than usual and thus load_entry throws an error when it cannot actually load the article since it has yet to be created. Is there any way to make sure an asynchronous function is finished without using callbacks?
Please let me know if there is any more info I can provide. I looked all over Google and couldn't find anything that really answered my question, since most solutions just say "use a callback!"
Use what is known as a promise
You can read more about it here.
There are lots of great libraries that can do this for you.
Q.js is one I personally like and it's widely used nowadays. Promises also exist in jQuery among many others.
Here's an example of using a q promise with an asynchronous json-p call: DEMO
var time;
$.ajax({
dataType: 'jsonp',
type: 'GET',
url: "http://www.timeapi.org/utc/now.json",
success: function (data) {
time = data;
},
error: function (data) {
console.log("failed");
}
})
.then(function(){ // use a promise library to make sure we synchronize off the jsonp
console.log(time);
});
This is definitely the kind of thing you want a callback for. Barring that, you're going to have to write some kind of callback wrapper that polls the database to determine when it has finished creating the relevant records, and then emits an event or does some other async thing to allow the test to continue.
Since the only native way to do asynchronous things are: setTimeout, setInterval and addEventListener, and they all take a callback you will eventually have to use a callback somewhere.
However, you can hide that by using Promises/A, also known as Deferreds.
Your code could look like this:
db.create_entry("foo", data).done(function (entry) {
db.delete_entry(entry).done(function () {
console.log("entry is deleted");
});
});
Using then-chaining:
db.create_entry("foo", data).then(function (entry) {
return db.delete_entry(entry);
}).done(function () {
console.log("entry is deleted");
});;
I found a solution that works. What I did was to add a callback to my function (next) and only call it if it's specified (i.e., for the tests):
//more stuff above
article.save(function(err){
if (!err) {
console.log(req.user.username + ' wrote ' + article.slug)
return next() || res.redirect('/admin')
}
return next(err) || res.render('articles/entry_form', {
title: 'New Entry',
article: article,
})
})
This way, when I run the actual server and no callback is specified, it won't throw an error, because it will simply return the res.render statement.
Supposed, I have a async function in Node.js, basically something such as:
var addAsync = function (first, second, callback) {
setTimeout(function () {
callback(null, first + second);
}, 1 * 1000);
};
Now of course I can call this function in an asynchronous style:
addAsync(23, 42, function (err, result) {
console.log(result); // => 65
});
What I am wondering about is whether you can make it somehow to call this function synchronously. For that, I'd like to have a wrapper function sync, which basically does the following thing:
var sync = function (fn, params) {
var res,
finished = false;
fn.call(null, params[0], params[1], function (err, result) {
res = result;
finished = true;
});
while (!finished) {}
return res;
};
Then, I'd be able to run addAsync synchronously, by calling it this way:
var sum = sync(addAsync, [23, 42]);
Note: Of course you wouldn't work using params[0] and params[1] in reality, but use the arguments array accordingly, but I wanted to keep things simple in this example.
Now, the problem is, that the above code does not work. It just blocks, as the while loop blocks and does not release the event loop.
My question is: Is it possible in any way to make this sample run as intended?
I have already experimented with setImmediate and process.nextTick and various other things, but non of them helped. Basically, what you'd need was a way to tell Node.js to please pause the current function, continue running the event loop, and getting back at a later point in time.
I know that you can achieve something similar using yield and generator functions, at least in Node.js 0.11.2 and above. But, I'm curious whether it works even without?
Please note that I am fully aware of how to do asynchronous programming in Node.js, of the event loop and all the related stuff. I am also fully aware that writing code like this is a bad idea, especially in Node.js. And I am also fully aware that an 'active wait' is a stupid idea, as well. So please don't give the advice to learn how to do it asynchronously or something like that. I know that.
The reason why I am asking is just out of curiosity and for the wish to learn.
I've recently created simpler abstraction WaitFor to call async functions in sync mode (based on Fibers). It's at an early stage but works. Please try it: https://github.com/luciotato/waitfor
using WaitFor your code will be:
console.log ( wait.for ( addAsync,23,42 ) ) ;
You can call any standard nodejs async function, as if it were a sync function.
wait.for(fn,args...) fulfills the same need as the "sync" function in your example, but inside a Fiber (without blocking node's event loop)
You can use npm fibers (C++ AddOn project) & node-sync
implement a blocking call in C(++) and provide it as a library
Yes I know-you know - BUT EVER-EVER-EVER ;)
Non-Blocking) use a control flow library
Standard propmises and yield (generator functions) will make this straigthforward:
http://blog.alexmaccaw.com/how-yield-will-transform-node
http://jlongster.com/A-Study-on-Solving-Callbacks-with-JavaScript-Generators