Can you intercept and create a AxiosPromise within a function? - javascript

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.
}))
}

Related

Why to use Promise.resolve().then()?

While reading Angular's code for the directive RouterLinkActive, I found a curious usage of Promise.resolve:
private update(): void {
...
Promise.resolve().then(() => {
const hasActiveLinks = this.hasActiveLinks();
if (this.isActive !== hasActiveLinks) {
...
}
});
}
In which case is it useful to use Promise.resolve().then() instead of just executing the code that is inside of the then?
I've seen usages of setTimeout(() => defeferedFunction()) but first time seeing it with Promise.resolve.
The Angular framework does this a lot. It is used to prevent an error that you will see a lot (sorry don't know the actual wording of the error) something like 'The value was changed after it was checked'. This error happens if the Change Detector checked a value and then it was changed in the same cycle.
The Promise.resolve() pushes this code on to the microtask queue so it is executed after the JavaScript stack is empty. This prevents the error.
If you want your function to return a promise, but you only conditionally run code that involves promises, you may want to use this pattern.
Alternatively, just use async/await syntax.
So for example, if you have a function to fetch data, but the data may be cached, you can do something like
function fetchData() {
return Promise.resolve().then(function() {
if (cache && cache.data) return cache.data;
// if we're here, the data isn't cached.
return database.getData();
})
}
This way, it ALWAYS returns a promise that always resolves with the data you want, even when you're using the sychronous cache code
In simple terms, inside a then handler function:
A) When x is a value (number, string, etc):
return x is equivalent to return Promise.resolve(x)
throw x is equivalent to return Promise.reject(x)
B) When x is a Promise that is already settled (not pending anymore):
return x is equivalent to return Promise.resolve(x), if the Promise was already resolved.
return x is equivalent to return Promise.reject(x), if the Promise was already rejected.
C) When x is a Promise that is pending:
return x will return a pending Promise, and it will be evaluated on the subsequent then.

JavaScript not delivering promise

I have two functions. function1() takes more time to complete than function2() because it does a fetch request. I need them to be launched in this order, but the results of function2() are the first that are displayed on the HTML DOM. So, I tried to resolve this with promises. I made the first function a variable, and created the following code:
let promise1 = function1() {
fetch()
.then(do x)
.then(display x to HTML DOM)
return 0;
};
function2(a) {
// use the a;
// display some things to the HTML DOM based on `a`
}
promise1.then((a) => {
function2(a);
});
Originally, these two functions don't need to interact with one another, but in order to make this work with promises, I created an artificial need by using that return statement. However, this doesn't work: I get a TypeError: promise1.then is not a function error. I skimmed through the 'Learn more' webpage, but those scenarios don't apply here.
I am quite new to JS and a neophyte to promises. Am I missing something?
You just need to return the promise returned from fetch in your first code block:
let promise1 = function1() {
return fetch()
.then(do x)
.then(() => {
//returns also need to be async
return 0;
});
//don't do this
// return 0;
// return inside the then() above
};
function2(a) {
// use the a;
// display some things to the HTML DOM based on `a`
}
promise1.then((a) => {
function2(a);
});
To explain this in greater detail; Your fetch runs async. So any subsequent functions will not wait (block). fetch returns a Promise that allows you to chain subsequent functions when the async function finishes. So to run anything after fetch you need to consume the Promise that fetch returns. then is a function of the Promise object, not fetch itself, to consume the promise (call then on the promise object) you need to return it first, hence return fetch().... How do I return the response from an asynchronous call? goes into this in detail
To address this, you will need to ensure function1 returns a promise object.
By returning a promise object, this allows you to "chain" subsequent .then() handlers off of calls to that function (ie promise1) as you are trying to do.
So in the case of your specific problem, you would want to do something like this:
let promise1 = function1() {
return fetch('/some/url').then(function (response){
// Do response processing logic here
return response;
}).then(function (data) {
//Chain any other data/response processing
return data;
});
};
The key thing to remember here is to return the call to fetch, as well as return data in each then handler that you chain to the call to fetch.
Hope that helps!

a jQuery promise that always succeeds?

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

Javascript pattern for handling finished promise

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.

How do I create a class that processes two promises and then returns a promise?

I want to create a class whose duty is to poll data sources, collate information into an array of 'alert' objects, and then deliver a subset of those alerts to any other class that wants them.
Because polling happens asynchronously (I'm requesting data from a web service) then I assume that what I actually need to return is a promise which, when fulfilled, will give the correct subset of Alert objects.
But clearly I don't understand how to do this, because the method that is supposed to return the promise returns something else.
Here's my code so far. As you can see, I'm trying to store the promise in an instance attribute and then retrieve it:
export class AlertCollection {
constructor() {
this.alerts = null;
}
// poll the data sources for alert data; store a promise that resolves
// to an array of alerts
poll() {
this.alerts = this.pollTeapot()
.then( (arr) => {this.pollDeliverance(arr);} );
}
// return a promise that fulfils to an array of the alerts you want
filteredAlerts(filter) {
return this.alerts; // not filtering for now
}
// return a promise that fulfills to the initial array of alerts
pollTeapot() {
let process = (json) => {
json2 = JSON.parse(json);
return json2.map( (a) => new Alert(a) );
};
message = new MessageHandler("teapot", "alerts")
return message.request().then( (json) => {process(json);} );
}
// Modify the alerts based on the response from Deliverance.
// (But for the time being let's not, and say we did.)
pollDeliverance(alerts) {
return alerts;
}
}
message.request() returns a promise from the web service. That works. If I snapshot the process function inside pollTeapot() I get the right data.
But, if I snapshot the return value from filteredAlerts() I don't get that. I don't get null either (which would at at least make sense, although it would be wrong.) I get something like { _45: 0, _81: 0, _65: null, _54: null }.
Any pointers would be very much appreciated at this point. (This is in React Native, by the way, if that helps.)
I am not sure if I understood your problem fully, but I will try to give you an generic solution to chaining promises one after another.
someAsyncFunction().then(dataFromAsync1 => {
return anotherAsyncFunction(dataFromAsync1).then(dataFromAsync2 => {
return doSomethingWithData(dataFromAsync1, dataFromAsync2);
});
});
This is going to be a hard one to describe - I have a working example but it's convoluted in that I've had to "mock up" all of the async parts, and use function classes rather than the class keyword - but the idea is the same!
There are 2 parts to this answer.
It does not make sense to store alerts as an instance variable. They are asynchronous, and wont exist until after the async calls have completed
You'll need to chain all of your behaviour onto the initial call to poll
In general, you chain promises on to one another like this
functionWhichReturnsPromise()
.then(functionPointer)
.then(function(result){
// some functionality, which can return anything - including another promise
});
So your code would end up looking like
var alertCollection = new AlertCollection()
alertCollection.poll().then(function(alerts){
//here alerts have been loaded, and deliverance checked also!
});
The code of that class would look along the lines of:
export class AlertCollection {
constructor() {
}
// poll the data sources for alert data; store a promise that resolves
// to an array of alerts
poll() {
return this.pollTeapot()
.then(filteredAlerts)
.then(pollDeliverance);
}
// return a promise that fulfils to an array of the alerts you want
filteredAlerts(alerts) {
return alerts; // not filtering for now
}
// return a promise that fulfills to the initial array of alerts
pollTeapot() {
let process = (json) => {
json2 = JSON.parse(json);
return json2.map( (a) => new Alert(a) );
};
message = new MessageHandler("teapot", "alerts")
return message.request().then(process);
}
// Modify the alerts based on the response from Deliverance.
// (But for the time being let's not, and say we did.)
pollDeliverance(alerts) {
return alerts;
}
}
A few notes
filteredAlerts can do whatever you like, so long as it returns an array of results
pollDeliverance can also do whatever you like - if it needs to call another async method, remember to return a promise which resolves to an array of alerts - perhaps updated from the result of the async call.
I have created a JSFiddle which demonstrates this - using a simple getJSON call to replicate the async nature of some of this. As I mentioned, it is convoluted, but demonstrates the process:
Live example: https://jsfiddle.net/q1r6pmda/1/

Categories