Asynchronous JavaScript - Callbacks vs Deferred/Promise [duplicate] - javascript

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
What are the differences between Deferred, Promise and Future in Javascript?
Lately I've been making an effort to improve the quality of my JavaScript applications.
One pattern I've adopted is to use a separate "data context" object to load data for my application (previously I was doing this directly in my view models).
The following example returns data that is initialized on the client:
var mockData = (function($, undefined) {
var fruit = [
"apple",
"orange",
"banana",
"pear"
];
var getFruit = function() {
return fruit;
};
return {
getFruit: getFruit
}
})(jQuery);
In most cases we'll be loading data from the server so we can't return an immediate response. It seems I have two options for how we handle this in our API:
Using a callback
Returning a promise.
Previously I'd always used the callback approach:
var getFruit = function(onFruitReady) {
onFruitReady(fruit);
};
// ...
var FruitModel = function(dataContext, $) {
return {
render: function() {
dataContext.getFruit(function(fruit) {
// do something with fruit
});
}
};
};
However, I can see how it's possible to end up in callback hell, especially when building complex JavaScript applications.
Then I came across the Promises design pattern. Instead of requiring the caller to supply a callback, I instead return a "promise" that can be observed:
var getFruit = function() {
return $.Deferred().resolve(fruit).promise();
};
// ...
dataContext.getFruit().then(function(fruit) {
// do something with fruit
});
I can see obvious benefits of using this pattern, especially since I can wait on multiple deferred objects which could be very useful when loading initialization data for a single page application.
However, I'm keen to understand the pros and cons of each pattern before I start to use either in anger. I'm also interested in whether this is the direction other libraries are going in. It seems to be the case with jQuery.
Here's a link to the fiddle I'm using for testing.

Promises also rely on callbacks behind the scene, so it's not really one vs. the other.
The benefit of callbacks is that they are easy to implement with plain JavaScript (for example in ajax calls).
Promises require an additional abstraction layer, which usually means that you'll rely on a library (not an issue in your case as you are already using jQuery). They are perfect when you deal with multiple async calls in parallel.

From reading the jQuery docs that #Pointy linked to, it sounds like the difference is that the Deferred API allows you to specify more than one function to be called when your request completes:
As of jQuery 1.5, the error (fail), success (done), and complete (always, as of jQuery 1.6) callback hooks are first-in, first-out managed queues. This means you can assign more than one callback for each hook. See Deferred object methods, which are implemented internally for these $.ajax() callback hooks.
See also: deferred.then()

Related

removing promise dependencies

I'm aware of the power of promises, however I have several old functions that are synchronous:
function getSomething() {
return someExternalLibrary.functionReturnsAValue()
}
console.log(getSomething()); // eg prints 'foo'
Unfortunately, when someExternalLibrary updated, it has removed functionReturnsAValue() and has lumped me with functionReturnsAPromise():
function getSomething() {
return someExternalLibrary.functionReturnsAPromise()
}
console.log(getSomething()); // now prints '[object]'
This of course, breaks absolutely everything written that depends on what used to be a simple value.
Obviously, I'd prefer two things:
ask the original library to keep a synchronous return value. (Not going to happen -- b/c they have refused)
A way to actually wait for a value
I have read numerous articles on why promises are great, ad nauseam, but the simple fact is: If I embrace promises, all I really do is shuffle promises onto some other part of the code, which then must deal with the promise of a value...
Is there a way (in nodejs) to actually wait for a promise to get itself together?
The best I can find is to use coroutines and yield, but really, it's still passing the buck. To be clear, I want the function getSomething to continue to return a value. Is there a way to do it?
Clearly, I fear I've misunderstood something about Promises...
The app is for non-browser implementations and runs purely from the command line. I've been trying to understand how bluebird's reflect() might help, to no avail.
(Yes, I'm aware this question has been asked many times in various formats, but I can't find a suitable answer to the core issue. If anything, I'm looking for the opposite of this question. The closest related (but unhelpful) question I can find is: Managing promise dependencies.)
There's the concept of generator functions. These are a special kind of function in both syntax (asterisk notation) and semantics. Unlike regular functions, generator functions return something that's also new to ECMAScript: iterators. Iterators happen to be objects made specifically to be iterated on, e.g. with the all new for...of loop. They can be also iterated on manually by calling their 'next' method. Each such call produces an object containing two properties: 'value' (iterator's current value) and 'done' (a boolean indicating whether we reached the last value of the iterable). However, the best thing about generator functions is their ability to suspend their execution each time a keyword 'yield' is encountered. Let's have a glimpse of how it all works together:
'use strict';
let asyncTask = () =>
new Promise((resolve, reject) => {
if (Math.random() > 0.5) {
resolve(1);
} else {
reject(new Error('Something went wrong'));
}
});
let makeMeLookSync = fn => {
let iterator = fn();
let loop = result => {
!result.done && result.value.then(
res => loop(iterator.next(res)),
err => loop(iterator.throw(err))
);
};
loop(iterator.next());
};
makeMeLookSync(function* () {
try {
let result = yield asyncTask();
console.log(result);
} catch (err) {
console.log(err.message);
}
});
The short answer
I am told repeatedly: You can't undo functions that have been promisified.
Edit: An upcoming solution
It appears that the ES2017 (although still draft), goes a long way in making promisified code easier to work with:
https://ponyfoo.com/articles/understanding-javascript-async-await
It seems that there is also a node library ready for this support too: https://github.com/normalize/mz.
Using this methodology, having apis converted to Promises won't be so bad (although it still appears that promises still poison the rest of the codebase):
const fs = require('mz/fs')
async function doSomething () {
if (await fs.exists(__filename)) // do something
}
The rest of this answer is just a general commentary on the problem.
Why we need a solution
Let's start with a sample piece of traditional synchronous code, in 3 flavours from more 'older-fashioned' to 'newer':
This is the traditional javascript way, requiring exception based programming to handle unexpected errors:
function getSomething() {
if (someproblem) throw new Error('There is a problem');
return 'foo';
}
However, adding try/ catch statements becomes very laborious and tedious, very quickly.
With the advent of node.js, callbacks were made popular, which nicely circumvented the issue, since each caller was explicitly forced to deal with error conditions in the same callback. This meant less errors in the caller's code:
function getSomething(callback) {
if (callback) {
if (someproblem)
callback(new Error('There is a problem'), null);
else
callback(null, 'foo');
}
return 'foo';
}
Then, the after some teething issues, node.js quickly proved itself for server-side communications, and people were amazed at the speed that asynchronous solutions provided. Node application frameworks like Express and Meteor grew, which focused on this.
Unfortunately, using the same callback scheme quickly became troublesome and the developers dealing in asynchronous code started using Promises in an effort to linearize the code, to make it readable, like the traditional (try/catch) code was.
The problem is that it got evangenlized too much. Everyone started thinking that Promises are the way to go. Personally, I call it a poison on a codebase. Once you have anything that uses Promises, your whole codebase must become asynchronous. This is not always a sensible nor a practical solution, IMHO.
The worst of all side effects is that the above function, even though it is completely synchronous, can be written in Promises too:
var bluebird = require('bluebird');
function getSomething() {
// IMHO, this is ridiculous code, but is increasingly popular.
if (someproblem) return Promise.reject(new Error('There is a problem'));
return Promise.resolve('foo');
}
For those who doubt this is a problem, perhaps should look at the SO question: How do I convert an existing callback API to promises?. Pay particular attention to #3, Node-style callback.
So, for anyone who cares, I would like to suggest that there needs to be a 'pill' for Promises. I urge that we need more than promises: we need results, and sometimes in a timely manner.
Take a look at the default node.js api. It does not use Promises. It also provides both synchronous and asynchronous calls to appropriate parts of the api (eg File System).
For those of you who feel tempted to downvote this answer: that is your prerogative, but there are clear issues on when Promises are not the answer, and I feel strongly that there are cases when we need to be able to re-synchronize decoupled code.
I also apologize for this 'blog-post' styled answer.

How to wrap jsonP callback in native javascript Promise?

I'm playin with native Promise to combine a bunch of XmlHttpRequests into one result and I think I got it working, see http://jsfiddle.net/pjs06hdo/
(random calls to flickr api, see the console for what's actually going on in which order)
There might be shorter implementations but with this code I can understand what's going on.
But then there comes the stupid JSONP :-( as it turns out the actual target site does not allow Cross-site requests and I have to use a provided jsonP endpoint (again simulated with flickr) And here I'm stuck: that stupid global callback does not fit into my basic understanding of Promise
I think the solution has to do with explanations in How do I convert an existing callback API to promises?.
I tried to implement this but it works only partially: http://jsfiddle.net/b33bj9k1/ There is no actual output, only console messages, sorry. But there you can see that there are three calls to create the promises but the resolve(), the jsonFlickrApiAsync() gets called only once.
What would be the right way to handle jsonP callbacks with Promise so I can have an Promise.all() to deal with the results as in the XmlHttpRequest version above?
No jQuery please - I want to understand whats really going
This is not a problem with promises, this is a problem with JSONP. Since it uses global callbacks, you need to use different callbacks - with different names - for each request. For Flickr that means you have to use their jsoncallback url parameter. The parameter name may vary for your actual endpoint.
However, your use of promises is indeed weird. Typically you'd use one promise per request, to represent that request's result. You are intentionally creating only one global promise, which cannot work.
function loadJSONP(url, parameter="callback") {
var prop = "loadJSONP.back" + loadJSONP.counter++;
var script = document.createElement("script");
function withCleanUp(r) {
return (x) => {
loadJSONP[prop] = null;
document.head.removeChild(script);
r(x);
}
}
return new Promise((resolve, reject) => {
loadJSONP[prop] = withCleanUp(resolve);
script.onerror = withCleanUp(reject);
// setTimeout(script.onerror, 5000); might be advisable
script.src = url+"&"+parameter+"="+prop;
document.head.appendChild(script);
});
}
loadJSONP.counter = 0;

Using generator function next() as a callback in node.js

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.

IndexedDb : read and return the object

Here is the code I'm using to retrieve informations from localdb :
function getById(idObject, typeObjectStore, cb_function)
{
idObject = parseInt(idObject);
var objectStore = db.transaction(typeObjectStore).objectStore(typeObjectStore);
var request = objectStore.get(idObject);
request.onerror = function(event) {
console.log('Error when loading ' + typeObjectStore);
};
request.onsuccess = function(event) {
cb_function(request.result);
};
}
I've read a lot of documentations and I can see that the retrieved content is usually printed directely in the cb_function (callback function).
I'd like to know if it's possible to return the object directly, so I would be able to exploit it in the rest of my code?
Doing something like this doesn't work, but you'll get what I'd like to do :
request.onsuccess = function(event) {
return(request.result);
};
I'm afraid it's not possible considering the asynchronous way of retrieving the data, but I'd like to have a confirmation.
Thank you in advance.
Regards,
Bdloul
It is not possible to return the value in this manner. The callback function is called asynchronously because request.onsuccess is called asynchronously. A return statement is synchronous. You need to know how to work with asynchronous Javascript (AJAX) in order to use indexedDB. Writing AJAX is primarily accomplished with passing callbacks, a continuation passing style (CPS).
Not everyone likes CPS, but it is a very powerful and simple way to express asynchronous operations in code, and sooner or later in every JavaScript programmer's career, you should learn about it.
If you are absolutely set on trying to return something, you can return a promise. Promises are not simple to learn how to use and will take a lot of understanding so proceed with caution.
Personally (my subjective opinion) I would not recommend trying to use Promises. I recommend learning more about Function.prototype.bind. You can use bind to avoid writing nested functions. Bind is also difficult to learn how to use but it also presents an elegant way to avoid nested functions.

What is the correct way to implement decoupled code / callbacks in Javascript?

I have several different JavaScript objects with methods that i want to keep separate. However, I need some type of observer, callback, or plugin design so that I can trigger these methods at the correct time.
For example, A.Foo() should almost always run after B.Bar() - but I do not want to place a call to A.Foo() inside B.Bar() because there are those odd times it should not run after B.Bar(). That would be coupling my code and I know that's bad.
I want to approach this problem as if B.Bar() is just doing it's job and does not ever plan on knowing about A.Foo() or any other function that might want to tag along with it. In other words, plugin support for future developers.
How do you design modular, non-coupled code with observer or event based callbacks in Javascript?
It depends whether your scenario is really event like in nature, or more of an asynchronous operation:
Event Based
Using something like microevent you could do the following. This pattern is really common and simple, so I suggest that at some point you have a go at implimenting this yourself, as it's great from an understanding point of view.
MicroEvent.mixin(A);
A.Foo = function () {
//Do stuff A needs to do, then:
A.trigger('foo-complete');
};
//Somewhere nice and separate and decoupled
A.bind('foo-complete', B.Bar);
If you need to do more complex things like filter the events, map the events etc. Reactive Extensions (which are based of the C# libraries) are really powerful, but that's a big library to load in if you don't need the functionality.
Async operation
Using Callbacks
This can be done using callbacks, which is great for fairly simple operations:
A.Foo = function (cb) {
//Do stuff A needs to do, then:
if (cb) cb();
};
A.Foo(B.Bar);
Using promises
These initially look more complicated, but they are easilly composable, and have nice ways of handling errors built in.
Using Q which is one of the more popular promise libraries (again, there are loads of these):
A.Foo = function () {
var def = Q.defer();
setTimeout(function () {
//Simulate async operation
def.resolve();
}, 1000);
return def.promise;
};
A.Foo().then(B.Bar).end();
Using control flow libraries
Control flow libraries (arguably promises are a special case of this) aim to help you compose operations that are based on the callback system.
Dojo allows you to listen to function calls.
dojo.connect("foo", A, function(){
B.bar();
});

Categories

Resources