I run into this every now and then:
return somethingThatReturnsAPromise()
.then((response) => {
soSomethingg(); // Eg; update the UI
return response;
});
Now I'm looking for something that is not expected to return anything and won't change the promise chain if I forget that:
return somethingThatReturnsAPromise()
.whatImLookingFor((response) => {
doSomething(); // Eg; update the UI
})
.then((response) => {
// and this one should still be able to access response
});
Maybe this goes against the idea of promises, but for me, it's a bit inconvenient since I can't pass arbitrary functions.
One idea is to compose a function:
const sideEffect = (callback) => {
return (response) => {
callback(response);
return response;
};
};
And I could use it as
return somethingThatReturnsAPromise()
.then(sideEffect(doSomething));
But I'd prefer something instead of then is there something like that?
Note: I'm working with Angular 1.x so I need something like for that.
I would assume that you're not really writing .then().then(), because you could collapse that into a single .then, but that your concern is really about returning the promise and having some external code add another then to the chain. In that case do this:
let p = somethingThatReturnsAPromise();
p.then(() => doSomething());
return p;
This allows the caller to attach additional thens to the original promise instead of chaining off of your .then, thereby receiving the original promise's value. This is called branching the promise chain.
Maybe this goes against the idea of promises
Slightly, promise chains are pipelines where then handlers transform things at each stage. But it's perfectly valid to want to pass through the value unchanged.
One idea is to compose a function:
Indeed the first thing that came to mind, and how I'd do it.
But I'd prefer something instead of then is there something like that?
There isn't. You could add it for your own projects (I wouldn't in a library) by adding it to Promise.prototype. Or you could give yourselve a Promise subclass and add it there.
With a Promise sublass you'd do something like:
return MyPromise.resolve(somethingThatReturnsAPromise())
.thenSide(soSomethingg); // Eg; update the UI
...where thenSide is your method that's then but passing the original value back unchanged, e.g.:
class MyPromise extends Promise {
thenSide(callback) {
this.then(callback);
return this;
}
}
or
class MyPromise extends Promise {
thenSide(callback) {
this.then(callback);
return MyPromise.resolve(this);
}
}
...depending on whether you're bothered about thenSide returning the same promise (since then always returns a new one).
As far as I know (I could well be wrong) the wrapper method for "pass-through" side-effects is an idiomatic way to do what you want.
Alternatively (if you need the same response in multiple places) you can break up the promise chain when you encounter a situation like this.
Related
I want to change a function that returns an AxiosPromise that currently works like so:
example(){
return api.get(url);
}
Where api.get returns an object of type AxiosPromise<any>
To something like this, where I can modify the object the original promise resolves:
example(){
if(condition){
newPromise = new AxiosPromise<any>((res, rej) => {
api.get(url).then( x => {
x.data = 3;
res(x);
}
};
return newPromise;
} else return api.get(url);
}
This example is based on my knowledge on how you use the normal Promise object but from what I can see AxiosPromise can't be used in this way.
Goal is to be able to modify the response if the condition is true but not have to refactor everywhere the function is currently being used.
My suggestion isn't wholly dissimilar to what you're already doing. You're using a regular promise now and my solution involves it as well. The unforeseen side effects you mention aren't likely to spring from the type of promise you return, but rather from what that promise resolves to i.e. (axios response object). So long as you keep it intact, I doubt all those other calls to your function will have any issues. As such, here is how I would modify your example function. In its original it looks like so:
example(){
return api.get(url);
}
And here is how you call it:
example().then(({data}) => console.log(data))
Ok. So far so good, but for starters I would wrap your axios call into a regular promise:
example(){
return Promise.resolve(axios.get(url).then(response => response))
}
This change does not change a thing on the caller side. You would still call it like so:
example().then(({data}) => console.log(data))
Now you want to add a condition to the function that, if true, modifies the response in some way. So here it is:
example(condition){
return Promise.resolve(axios.get(url).then(response => {
if(condition === true) response.data.somekey = 'Changed';
return response; //The response object is intact save for the one change.
}))
}
I have a sequence of events I'm trying to align, but I don't understand how to resolve a promise after a single line of asynchronous code. In this case, I'm making an API call which resolves to an array of objects. I'm then using Object.assign() to convert the array of objects into an object of objects. Once completed, I then want to call a function which does something with that new object of objects. The conversion process takes some time, and I'm not sure how to postpone my function call until after the object has been converted. My non-functioning code is given below.
this.queryCodes().then(() => {
new Promise((resolve) => {
resolve(() => {
// This code isn't executing.
this.floorCodesLookup = Object.assign({}, ...this.floorCodes);
});
}).then((data) => {
this.resolveCodesToDescriptions();
});
});
resolve should be given the result of your asynchronous operation, not the code to execute. The code you want to execute shouldn't go in the resolve argument, but instead in the callback you give to the promise constructor. So for your snippet:
this.queryCodes().then(() => {
return new Promise((resolve) => {
// Put the code you want to execute here...
// Call resolve when your work is done, and pass it the result if there is one.
resolve(/* maybe result here */);
}).then((data) => {
this.resolveCodesToDescriptions();
});
});
It's also worth noting that you don't need to use a promise constructor since you're already chaining off a promise to begin with. You could just:
this.queryCodes().then(() => {
// Do additional work here...
this.floorCodesLookup = Object.assign({}, ...this.floorCodes);
return this.resolveCodesToDescriptions();
});
resolve() doesn't take a callback as an argument. It takes the resolved value as an argument. Not sure where you got the callback notion from. That's why the callback is never called because that's not a supported feature of resolve().
Manually creating a promise inside a .then() handler is likely not needed and is probably an anti-pattern. You don't show enough of the real code here to understand what is going on, but this is likely way more complicated than it needs to be. From the code you show, I think you can just do this:
this.queryCodes().then(() => {
this.floorCodesLookup = Object.assign({}, ...this.floorCodes);
this.resolveCodesToDescriptions();
});
I have this code jQuery code fragment:
$.get('/api/' + currentPage).done(function(data) { ... })
.fail(...)
I want to replace $.get('/api/'+currentPage) with a promise that always succeeds and returns a specific value for data. Something like:
let myData = { ... } // value of data I want to pass to the done function
(new AlwaysSucceeds(myData)).done(function(data) { ... })
.fail(...)
I could cobble up a dummy object, or I could extract out the done function but I want to keep changes to the code to a minimum.
Is there a way to do this?
UPDATE: To help clarify what's going, the code I am working with is (here). Normally this app is served from a nodejs server which implements the /api/... call, but I am converting it to be served
from a static page server. I know what is going to be returned from
the $.get call. To keep changes to the code clean I simply want to
change that line to:
let myData = {...}
// $.get('/api/' + currentPage) -- comment out the $.get call
(SOMETHINGHERE(myData)).done(function(data) {
The SOMETHINGHERE expression needs to implement .done(f)
which will call the function f with myData and then return
some object which implements .fail(...) which does nothing.
You can just replace $.get(...) with a function that returns a promise that is already resolved with the data you already have. And, the shortest way to get an already resolved jQuery promise, resolved with a particular value, is this:
$.when(myData).done(...)
The more text book way to do it in jQuery is:
$.Deferred().resolve(myData).done(...)
And, if you care to switch your logic to the the ES6 standard (instead of the non-standard jQuery promise behaviors), then you could use this:
Promise.resolve(myData).then(...).catch(...)
You can achieve this by implementing AlwaysSuceeds constructor function. Please see below example.
function AlwaysSucceeds(data) {
this.data = data;
}
AlwaysSucceeds.prototype.done = function(fn) {
fn(this.data);
return this;
}
AlwaysSucceeds.prototype.fail = function(fn) {
return this;
}
var myData = {
a: 1
};
(new AlwaysSucceeds(myData)).done(function(data) {
console.log(data)
}).fail(function(data){
})
Since jQuery Ajax functions just return $.Deferred objects, you can just substitute an immediately-resolved Deferred:
$.Deferred().resolve(myData).then(...)
In this particular case, if you want to make it easy to switch between synchronous and asynchronous code, and you have access to async/await, you can just use those directly:
try {
const data = await Promise.resolve($.get('/api/' + currentPage));
// code in done
} catch (err) {
// code in fail
}
would become
try {
const data = myData;
// code in done
} catch (err) {
// code in fail (never runs unless other code throws exceptions)
}
It's not clear what you actually want but be carufull using jQuery Deferred with native promises, the deferred has some non standard methods that native promises don't have.
So to be save I always assume there is a thenable, something that has a then with that you can pretty much do whatever you want.
jQuery Deferred do not behave like native promises either (depending on version):
$.Deferred().reject("hello world")
.then(
undefined
,x=>x
)
.then(
x=>console.log("Never happens",x)
)
Promise.reject("hello world")
.then(
undefined
,x=>x
);
.then(
x=>console.log("Well behaved",x)
);
Promise.resolve().then(x=>{throw "nope"})
.then(undefined,err=>console.warn(err));
$.Deferred().resolve().then(x=>{throw "nope"})//crashes
.then(undefined,err=>err);
So it will be saver to use native promises and polyfill with something that behaves like native.
To answer the question about non failing promise, if you want to make a request but return a default when it rejects and keep returning the same once resolves or rejects you can do:
const get = (p=>{
(url) => {
p = p ||
//return native promise or properly polyfilled one
Promise.resolve($.get(url))
.then(
undefined,
_=> {defaultobject:true}
);
return p;
}
})();
Your get function will return a native promise so no fail, done and other things that are non standard. Combining "promises" from different libraries and native promises it would be best to only use then
I am generating objects using external source with intention of processing them callback style. Unfortunately these callbacks can chain way beyond the stack limit of a browser and I have to keep in mind the worst case scenario of 500 for IE.
I have rewritten my code using $q.when
$scope.combinations.forEach(function(combination) {
chain = chain.then(function() {
return generateConfiguration(combination);
});
});
generateConfiguration if a function that returns a promise. It all works just fine, but what I want to do is add finally() at the end of the chain.
What I did so far is to have a tracker inside generateConfiguration that recognizes the last combination and triggers what should have been triggered by finally.
Is there a cleaner way to do it?
It depends on what you want to do.
You're creating a promise chain, so if you want a single finally for the chain, just add it at the end after building it with that forEach:
// ...your forEach here, then:
chain.finally(/*...*/);
If you want a finally on each promise from generateConfiguration, either give yourself a wrapper function to do that, or do it inside the forEach callback.
$scope.combinations.forEach(function(combination) {
chain = chain.then(function() {
return generateConfiguration(combination).finally(/*...*/); // <==
});
});
Side note: There's a more idiomatic way to build that chain, via reduce:
var chain = $scope.combinations.reduce(function(chain, combination) {
return chain.then(function() {
return generateConfiguration(combination);
});
}, Promise.resolve());
Adding a final finally (option #1 above):
var chain = $scope.combinations.reduce(function(chain, combination) {
return chain.then(function() {
return generateConfiguration(combination);
});
}, Promise.resolve())
.finally(function() {
// ...do stuff here...
});
Yes, there is: inject $q (i.e. the promise framework) and rew-rite to
$q.all($scope.combinations.map(function(combination) {
return generateConfiguration(combination);
})).finally(function(res) {
/* ... */
});
That way, you'll also fire off all generateConfiguration(s) in parallel (at least as many as possible - like there 's a limit on e.g. XHR). Anyways: $q.all(arrayOfPromises) might be what you've been looking for.
Background
I am trying to learn promises, and I have a promise chain I want to improve on.
Problem
While learning how to chain promises, I fail to see why anyone would rather return a promise instead of returning it's value.
Take the following example, which uses promise chaining:
let myObj = new MyClass();
myObj.getInfo()
.then(result => writeOutput(FILE_NAME, result))
.then(console.log(FILE_NAME + " complete"))
.catch(error => console.error(error));
class MyClass{
getInfo() {
return new Promise(function(fulfil, reject) {
fulfill("I like bananas");
});
}
Here I have to chain 2 times. But if I were to directly return the result from the method getInfo() instead of returning a Promise I could potentially do something like the following:
let myObj = new MyClass();
let str = myObj.getInfo();
writeOutput(FILE_NAME, str)
.then(console.log(FILE_NAME + " complete"))
.catch(error => console.error(error));
Questions
So as you can see I am a little confused.
Given that getInfo() is in fact async, is it possible to achieve a similar code to the one in my second code sample?
If it were possible, would it be a good idea? How would you do it?
You can only return a value from some function if that value is immediately available when that function is called (on the same tick of the event loop). Remember that return is synchronous.
If it is not available right away then you can only return a promise (or you can use a callback but here you are specifically asking about promises).
For a more detailed explanation see this answer that I wrote some time ago to a question asking on how to return a result of an AJAX call from some function. I explained why you cannot return the value but you can return a promise:
jQuery: Return data after ajax call success
Here is another related answer - it got downvoted for some reason but I think it explains a similar issue that you're asking about her:
Return value in function from a promise block
I fail to see why anyone would rather return a promise instead of returning it's value.
Because you don't have the value yet.
Given that getInfo() is in fact async, is it possible to achieve a similar code to the one in my second code sample?
If it's asynchronous, it must return a promise.
A syntax that avoids then calls (but still uses and produces promises) is made possible by async/await:
async function writeInfo(FILE_NAME) {
const myObj = new MyClass();
try {
const str = await myObj.getInfo();
await writeOutput(FILE_NAME, str);
console.log(FILE_NAME + " complete");
} catch (error) {
console.error(error);
}
}
writeInfo("…");
The purpose of a promise is to say: "The data will be there sometime, just continue with your coding for now", so that your page won't get stuck for example. Basically you are using Promises for example to load something from the server.
Another way of looking at the first question ("why would anyone..."):
When you call .then or .catch on a promise, the pending promise returned from the call is appended to the end of an existing promise chain. (Allowing the "existing chain" might only contain one promise).
When you resolve (not "fulfill", you can't fulfill a promise with a promise) a promise with a promise, the promise used in resolution is inserted at the head of what remains of the promise chain.
The promise used in resolution must be fulfilled with a value before the next .then or .catch clause in the chain is called.