Alternative to using callback function in JavaScript? - javascript

I'm trying to have the second function be called after the scrolling animation of the first one finishes. I've seen that callback functions are used in similar situations however they seem to be more cumbersome and difficult to read. Is there an simpler or more clean way to achieve the same result?
$('#nav_experience').click(function scrollToExperience(){
$('html, body').animate({
scrollTop: $('#Experience').offset().top
}, 500);
})
function navbarExperienceActive(){
scrollToExperience(function(){
$('#nav_profile').removeClass('active');
$('#nav_education').removeClass('active');
$('#nav_contact').removeClass('active');
$('#nav_experience').addClass('active');
});
}
Also I appreciate the second function is quite repetitive and there may be a more elegant way to write it, I'm relatively new to JavaScript.

One modern approach to situations where we used to use callbacks is to use promises, which are particularly effective when combined with async functions and await. (Conceptually, "promises" go by many names — promises, futures, deferreds....)
Promises themselves still use callbacks, but by providing a standardized form of passing around promises for future callbacks, they markedly improve code that used to pass callbacks around just as arguments.
Your scrollToExperience might look like this, for instance:
function scrollToExperience(){
return new Promise(resolve => {
$('html, body').animate({
scrollTop: $('#Experience').offset().top
}, 500, resolve);
});
}
navbarExperienceActive might use it like this:
function navbarExperienceActive(){
scrollToExperience().then(function(){
$('#nav_profile').removeClass('active');
$('#nav_education').removeClass('active');
$('#nav_contact').removeClass('active');
$('#nav_experience').addClass('active');
});
}
On the face of it, that hasn't changed much; you still have to pass a callback. But if navbarExperienceActive had to combine this with other asynchronous actions, chain off it in other ways, etc., using promises makes that simpler and more standard between codebases.
But if navbarExperienceActive were an async function, it could use await:
async function navbarExperienceActive(){
await scrollToExperience();
$('#nav_profile').removeClass('active');
$('#nav_education').removeClass('active');
$('#nav_contact').removeClass('active');
$('#nav_experience').addClass('active');
}
It still uses promises, but the code is written according to its simple logical flow rather than worrying about the fact that it has to wait for scrollToExperience to finish.
An async function always returns a promise; when you use await in the async function, if the value you pass it is a promise (hand-waves details about "thenables"), it returns its promise then settles that promise based on what happens to the promise you're awaiting (and/or the logic following it).
I should note that the promise returned by scrollToExperience above is a bit unusual, because it always fulfills the promise, it never rejects it. The code using its promise relies on that fact that it never rejects its promise.
In general, though, it's important to be sure to either handle promise rejection or pass the promise chain on to something else that will. That's another place where async functions make life simpler: They automatically propagate promise rejections, exactly the way synchronous functions automatically propagate exceptions.

Related

Why use a promise to wrap axios API calls? [duplicate]

I was writing code that does something that looks like:
function getStuffDone(param) { | function getStuffDone(param) {
var d = Q.defer(); /* or $q.defer */ | return new Promise(function(resolve, reject) {
// or = new $.Deferred() etc. | // using a promise constructor
myPromiseFn(param+1) | myPromiseFn(param+1)
.then(function(val) { /* or .done */ | .then(function(val) {
d.resolve(val); | resolve(val);
}).catch(function(err) { /* .fail */ | }).catch(function(err) {
d.reject(err); | reject(err);
}); | });
return d.promise; /* or promise() */ | });
} | }
Someone told me this is called the "deferred antipattern" or the "Promise constructor antipattern" respectively, what's bad about this code and why is this called an antipattern?
The deferred antipattern (now explicit-construction anti-pattern) coined by Esailija is a common anti-pattern people who are new to promises make, I've made it myself when I first used promises. The problem with the above code is that is fails to utilize the fact that promises chain.
Promises can chain with .then and you can return promises directly. Your code in getStuffDone can be rewritten as:
function getStuffDone(param){
return myPromiseFn(param+1); // much nicer, right?
}
Promises are all about making asynchronous code more readable and behave like synchronous code without hiding that fact. Promises represent an abstraction over a value of one time operation, they abstract the notion of a statement or expression in a programming language.
You should only use deferred objects when you are converting an API to promises and can't do it automatically, or when you're writing aggregation functions that are easier expressed this way.
Quoting Esailija:
This is the most common anti-pattern. It is easy to fall into this when you don't really understand promises and think of them as glorified event emitters or callback utility. Let's recap: promises are about making asynchronous code retain most of the lost properties of synchronous code such as flat indentation and one exception channel.
What's wrong with it?
But the pattern works!
Lucky you. Unfortunately, it probably doesn't, as you likely forgot some edge case. In more than half of the occurrences I've seen, the author has forgotten to take care of the error handler:
return new Promise(function(resolve) {
getOtherPromise().then(function(result) {
resolve(result.property.example);
});
})
If the other promise is rejected, this will happen unnoticed instead of being propagated to the new promise (where it would get handled) - and the new promise stays forever pending, which can induce leaks.
The same thing happens in the case that your callback code causes an error - e.g. when result doesn't have a property and an exception is thrown. That would go unhandled and leave the new promise unresolved.
In contrast, using .then() does automatically take care of both these scenarios, and rejects the new promise when an error happens:
return getOtherPromise().then(function(result) {
return result.property.example;
})
The deferred antipattern is not only cumbersome, but also error-prone. Using .then() for chaining is much safer.
But I've handled everything!
Really? Good. However, this will be pretty detailed and copious, especially if you use a promise library that supports other features like cancellation or message passing. Or maybe it will in the future, or you want to swap your library against a better one? You won't want to rewrite your code for that.
The libraries' methods (then) do not only natively support all the features, they also might have certain optimisations in place. Using them will likely make your code faster, or at least allow to be optimised by future revisions of the library.
How do I avoid it?
So whenever you find yourself manually creating a Promise or Deferred and already existing promises are involved, check the library API first. The Deferred antipattern is often applied by people who see promises [only] as an observer pattern - but promises are more than callbacks: they are supposed to be composable. Every decent library has lots of easy-to-use functions for the composition of promises in every thinkable manner, taking care of all the low-level stuff you don't want to deal with.
If you have found a need to compose some promises in a new way that is not supported by an existing helper function, writing your own function with unavoidable Deferreds should be your last option. Consider switching to a more featureful library, and/or file a bug against your current library. Its maintainer should be able to derive the composition from existing functions, implement a new helper function for you and/or help to identify the edge cases that need to be handled.
Now 7 years later there is a simpler answer to this question:
How do I avoid the explicit constructor antipattern?
Use async functions, then await every Promise!
Instead of manually constructing nested Promise chains such as this one:
function promised() {
return new Promise(function(resolve) {
getOtherPromise().then(function(result) {
getAnotherPromise(result).then(function(result2) {
resolve(result2);
});
});
});
}
just turn your function async and use the await keyword to stop execution of the function until the Promise resolves:
async function promised() {
const result = await getOtherPromise();
const result2 = await getAnotherPromise(result);
return result2;
}
This has various benefits:
Calling the async function always returns a Promise, which resolves with the returned value and rejects if an error get's thrown inside the async function
If an awaited Promise rejects, the error get's thrown inside the async function, so you can just try { ... } catch(error) { ... } it like the synchronous errors.
You can await inside loops and if branches, making most of the Promise chain logic trivial
Although async functions behave mostly like chains of Promises, they are way easier to read (and easier to reason about)
How can I await a callback?
If the callback only calls back once, and the API you are calling does not provide a Promise already (most of them do!) this is the only reason to use a Promise constructor:
// Create a wrapper around the "old" function taking a callback, passing the 'resolve' function as callback
const delay = time => new Promise((resolve, reject) =>
setTimeout(resolve, time)
);
await delay(1000);
If await stops execution, does calling an async function return the result directly?
No. If you call an async function, a Promise gets always returned. You can then await that Promise too inside an async function. You cannot wait for the result inside of a synchronous function (you would have to call .then and attach a callback).
Conceptually, synchronous functions always run to completion in one job, while async functions run synchronously till they reach an await, then they continue in another job.

Reusing an existing Promise object if exists

I have a Dialog object that will show, well, dialogs. There are many entry points to show dialogs, e.g. yesNo(), message(), confirm(), etc. However, all these methods basically call the same other method, called showSimpleDialog(title, message, buttons).
I'd like all these methods (showSimpleDialog too) to return a promise, but there's a snag:
yesNo() {
return new Promise((resolve, reject) => {
axios
.get(......)
.then(this.showSimpleDialog(...));
}
}
As you can see, I am prevented in the above example from either returning the promise that showSimpleDialog would make or by passing the instanced Promise to showSimpleDialog.
The former is impossible because we're already in a different Promise by the time we have access to it. The latter because the Promise object itself is not yet available within the constructor. Well, technically, in this particular case it is (exactly because we're already in a different Promise), but some of my entry functions are synchronous, some asynchronous and I simply can't use the same code pattern to achieve the same effect in both cases.
I investigated the thing and I found this, but the author suggests the approach is flawed and archaic to begin with.
So, what would be the correct approach to return a functioning Promise from ALL entry points while the entry points would still be free to reusing each other's Promises?
If I understand correctly, this.showSimpleDialog(...) also returns a Promise, right?
If you want yesNo() to return the Promise retunred by this.showSimpleDialog(...)
yesNo() {
return axios
.get(......)
.then(()=>{
return this.showSimpleDialog(...);
});
}
That being said, consider using async/await, especially when dealing with multiple sequential promises, if possible.
Your code is calling this.showSimpleDialog immediately (synchronously) without waiting for any promise to resolve (the axios one). This is because the code doesn't pass a function to the then method, but instead executes this.showSimpleDialog. This execution returns a promise (presumably), but then expects a function as argument, not a promise.
So you need to make sure to pass a callback to then, and let that callback return a promise. This way promises will be chained:
.then(() => this.showSimpleDialog(...));
It is also important to make that callback an arrow function, since you'll be referencing this, which is intended to be the this on which yesNo is called.

Difference Callback, Promise and Event JavaScript/React-Native <-> Java/Android

I am reading about native modules in React-Native and Android.
The documentation distinguishes between
callbacks
promises
events
I am having trouble understaind the difference between the three of them. What are the main differences and when is used what?
A callback is a function that you pass to another function, which will then call that when some async action is done:
somethingAsync(function callback(result) { /*...*/ });
You will see this pattern quite often, as everything ilelse relies on callbacks. If you however got many callbacks, the code gets hard to read and to maintain, especially when working with loops. For that, Promises were introduced, which wrap a callback function, and allow to attach callbacks to the Promise. That allows for some nice chaining:
const promise = new Promise(somethingAsync);
promise.then(function result() { /*...*/ });
Note that a promise only resolves once, while callbacks can be called multiple times.
Events are not really related to Promises, they allow you to attach a callback to an object that will get called when something happens, e.g. a button gets clicked:
button.onclick = function(event) { /*...*/ }

Is there a way to refactor and simplify this server side code? [duplicate]

I was writing code that does something that looks like:
function getStuffDone(param) { | function getStuffDone(param) {
var d = Q.defer(); /* or $q.defer */ | return new Promise(function(resolve, reject) {
// or = new $.Deferred() etc. | // using a promise constructor
myPromiseFn(param+1) | myPromiseFn(param+1)
.then(function(val) { /* or .done */ | .then(function(val) {
d.resolve(val); | resolve(val);
}).catch(function(err) { /* .fail */ | }).catch(function(err) {
d.reject(err); | reject(err);
}); | });
return d.promise; /* or promise() */ | });
} | }
Someone told me this is called the "deferred antipattern" or the "Promise constructor antipattern" respectively, what's bad about this code and why is this called an antipattern?
The deferred antipattern (now explicit-construction anti-pattern) coined by Esailija is a common anti-pattern people who are new to promises make, I've made it myself when I first used promises. The problem with the above code is that is fails to utilize the fact that promises chain.
Promises can chain with .then and you can return promises directly. Your code in getStuffDone can be rewritten as:
function getStuffDone(param){
return myPromiseFn(param+1); // much nicer, right?
}
Promises are all about making asynchronous code more readable and behave like synchronous code without hiding that fact. Promises represent an abstraction over a value of one time operation, they abstract the notion of a statement or expression in a programming language.
You should only use deferred objects when you are converting an API to promises and can't do it automatically, or when you're writing aggregation functions that are easier expressed this way.
Quoting Esailija:
This is the most common anti-pattern. It is easy to fall into this when you don't really understand promises and think of them as glorified event emitters or callback utility. Let's recap: promises are about making asynchronous code retain most of the lost properties of synchronous code such as flat indentation and one exception channel.
What's wrong with it?
But the pattern works!
Lucky you. Unfortunately, it probably doesn't, as you likely forgot some edge case. In more than half of the occurrences I've seen, the author has forgotten to take care of the error handler:
return new Promise(function(resolve) {
getOtherPromise().then(function(result) {
resolve(result.property.example);
});
})
If the other promise is rejected, this will happen unnoticed instead of being propagated to the new promise (where it would get handled) - and the new promise stays forever pending, which can induce leaks.
The same thing happens in the case that your callback code causes an error - e.g. when result doesn't have a property and an exception is thrown. That would go unhandled and leave the new promise unresolved.
In contrast, using .then() does automatically take care of both these scenarios, and rejects the new promise when an error happens:
return getOtherPromise().then(function(result) {
return result.property.example;
})
The deferred antipattern is not only cumbersome, but also error-prone. Using .then() for chaining is much safer.
But I've handled everything!
Really? Good. However, this will be pretty detailed and copious, especially if you use a promise library that supports other features like cancellation or message passing. Or maybe it will in the future, or you want to swap your library against a better one? You won't want to rewrite your code for that.
The libraries' methods (then) do not only natively support all the features, they also might have certain optimisations in place. Using them will likely make your code faster, or at least allow to be optimised by future revisions of the library.
How do I avoid it?
So whenever you find yourself manually creating a Promise or Deferred and already existing promises are involved, check the library API first. The Deferred antipattern is often applied by people who see promises [only] as an observer pattern - but promises are more than callbacks: they are supposed to be composable. Every decent library has lots of easy-to-use functions for the composition of promises in every thinkable manner, taking care of all the low-level stuff you don't want to deal with.
If you have found a need to compose some promises in a new way that is not supported by an existing helper function, writing your own function with unavoidable Deferreds should be your last option. Consider switching to a more featureful library, and/or file a bug against your current library. Its maintainer should be able to derive the composition from existing functions, implement a new helper function for you and/or help to identify the edge cases that need to be handled.
Now 7 years later there is a simpler answer to this question:
How do I avoid the explicit constructor antipattern?
Use async functions, then await every Promise!
Instead of manually constructing nested Promise chains such as this one:
function promised() {
return new Promise(function(resolve) {
getOtherPromise().then(function(result) {
getAnotherPromise(result).then(function(result2) {
resolve(result2);
});
});
});
}
just turn your function async and use the await keyword to stop execution of the function until the Promise resolves:
async function promised() {
const result = await getOtherPromise();
const result2 = await getAnotherPromise(result);
return result2;
}
This has various benefits:
Calling the async function always returns a Promise, which resolves with the returned value and rejects if an error get's thrown inside the async function
If an awaited Promise rejects, the error get's thrown inside the async function, so you can just try { ... } catch(error) { ... } it like the synchronous errors.
You can await inside loops and if branches, making most of the Promise chain logic trivial
Although async functions behave mostly like chains of Promises, they are way easier to read (and easier to reason about)
How can I await a callback?
If the callback only calls back once, and the API you are calling does not provide a Promise already (most of them do!) this is the only reason to use a Promise constructor:
// Create a wrapper around the "old" function taking a callback, passing the 'resolve' function as callback
const delay = time => new Promise((resolve, reject) =>
setTimeout(resolve, time)
);
await delay(1000);
If await stops execution, does calling an async function return the result directly?
No. If you call an async function, a Promise gets always returned. You can then await that Promise too inside an async function. You cannot wait for the result inside of a synchronous function (you would have to call .then and attach a callback).
Conceptually, synchronous functions always run to completion in one job, while async functions run synchronously till they reach an await, then they continue in another job.

Can a function either return a promise, or not?

So that the caller of the function (the user of the service) can use .then if he wants to do something with the information the function generates. If he doesn't care when it gets done, as long as it gets done sometime, he can just call the function without any .then infrastructure.
Will this work? I don't want to get into a situation where it will work in my tests, but in some obscure situation that doesn't happen very often, it will fail.
Hmm. I guess what I mean is this. If I am writing the routine that returns the promise, I have to say:
return new Promise(function (resolve, reject) { ... });
If my caller doesn't say:
.then(function () { ... }, function () { ... });
what will happen? I will at some point call resolve() or reject(), and resolve and reject won't be defined. Does the Promise constructor supply some default (do nothing) definition?
I suppose if I am a crazy person, I can say in my callee function:
(resolve || function () {})();
A function that returns a promise will do what you'd like it to do.
Given a function B():
If the user does not chain B().then(), they will get the answer eventually whenever it is done. It is up to them to handle the fact that they don't know when the value is populated. That is to be expected.
If the user does chain B().then(), they will have a nice and easy way to control what happens once the value is returned.
You do not need to worry about weird edge cases. A function that returns a promise is a clear and straightforward contract.
As with all functions in Javascript, the caller is free to ignore a return value. The Javascript garbage collector will take care of objects or values that are no longer in use.
So, if the caller of some async operation that returns a promise really doesn't care when it's done OR if there are errors, then the caller is free to just ignore the returned promise. Nothing bad happens (other than the fact that you may never know there are errors).
The part of your question that does not seem to be cool with this is where you say: "If he doesn't care when it gets done, as long as it gets done sometime". If you are ignoring async errors, then this may not actually get done sometime and you may never know that. In this case, it might be more appropriate to do:
someAsyncFunc(...).catch(function(err) {
console.err(err);
// so something meaningful with the error here
});
Unless you specifically need to wrap a legacy API, using the Promise constructor is an antipattern. Use the Promise factories instead, or if using bluebird, see if you can use bluebird's promisify on the legacy function.
You also seem to be misunderstanding what the parameters to the Promise constructor function argument are. They are not the callbacks, they are the functions that settle the Promise. The Promise itself will worry about notifying any callbacks, if they exist.
Also, if your function sometimes returns a Promise, and sometimes does not, you might crash any code that assumes it can call .then on your function's return value. Don't do that.
If your computation may be async, always return a Promise. If you want to return a value that is already settled, then return Promise.resolve(theValue).

Categories

Resources