Call backup endpoint in case get promise fails - javascript

I have a method returning a promise that either resolves or rejects.
myMethod() {
return new Promise((resolve, reject) => {
axiosClient
.get(endpoint)
.then((response) => {
resolve(response.data);
})
.catch((error) => {
reject(error);
});
});
}
Is there a way to return another promise in case this produces an error, calling a backup endpoint?? Something along the lines of:
myMethod() {
return new Promise((resolve, reject) => {
axiosClient
.get(endpoint)
.then((response) => {
resolve(response.data);
})
.catch(() => {
axiosClient
.get(backupEndpoint)
.then((response) => {
resolve(response.data);
})
.catch((error) => {
reject(error);
});
});
});
Edit: if this is a duplicate I have not been able to find something about it that is specific to this point. Could it be because it's a bad practice?
Edit2: thanks for the answers, maybe I should clarify better the flow I want to achieve:
get endpoint -> (if this fails) get fallback endpoint -> (if this fails aswell) reject the entire promise

From the above, one could simplify it doing with re-usability best practices,
as follows:
myMethod() {
const PromiseHandler = url => {
/**
* this function recevies url
* makes the request and return
* a reposne form the end point
*/
return axiosclient.get(url).then(res => res.data)
}
return PromiseHandler(endpoint)
.catch(() => {
return PromiseHandler(backupEndPoint)
}

Related

chaining promises in functions

I have a small problem, how to create a promise chain in a sensible way so that the makeZip function will first add all the necessary files, then create the zip, and finally delete the previously added files? (The makeZip function also has to return a promise). In the example below I don't call deleteFile anywhere because I don't know exactly where to call it. when I tried to call it inside the add file function to delete the file immediately after adding it, for some unknown reason the console displayed the zip maked! log first and then file deleted.
const deleteFile = (file, result) => {
new Promise((resolve, reject) => {
fs.unlink(`./screenshots/${file}`, (err) => {
if (err) return reject(err);
console.log(`${file} deleted!`);
return resolve();
});
});
};
const addFile = (file) => {
new Promise((resolve, reject) => {
try {
zip.addLocalFile(`./screenshots/${file}`);
console.log(`${file} added`);
return resolve();
} catch {
return reject(new Error("failed to add file"));
}
});
};
const makeZip = () => {
Promise.all(fs.readdirSync("./screenshots").map((file) => addFile(file)))
.then(() => {
return new Promise((resolve, reject) => {
try {
zip.writeZip(`./zip_files/supername.zip`);
console.log("zip maked!");
resolve();
} catch {
return reject(new Error("failed making zip"));
}
});
})
.catch((err) => console.log(err));
};
the main cause of this is that you are not returning the promises you are instantiating in your function calls. Also I have some cool suggestion to make that can improve you code cleanliness.
[TIP]: Ever checked the promisify function in NodeJS util package, it comes with node and it is very convenient for converting functions that require callbacks as arguments into promise returning functions., I will demonstrate below anyhow.
// so I will work with one function because the problem resonates with the rest, so
// let us look at the add file function.
// so let us get the promisify function first
const promisify = require('util').promisify;
const addFile = (file) => {
// if addLocalFile is async then you can just return it
return zip.addLocalFile(`./screenshots/${file}`);
};
// okay so here is the promisify example, realized it wasn't applicable int the function
// above
const deleteFile = (file, result) => {
// so we will return here a. So because the function fs.unlink, takes a second arg that
// is a callback we can use promisify to convert the function into a promise
// returning function.
return promisify(fs.unlink)(`./screenshots/${file}`);
// so from there you can do your error handling.
};
So now let us put it all together in your last function, that is, makeZip
const makeZip = () => {
// good call on this, very interesting.
Promise.all(fs.readdirSync("./screenshots").map((file) => addFile(file)))
.then(() => {
return zip.writeZip(`./zip_files/supername.zip`);
})
.then(() => {
//... in here you can then unlink your files.
});
.catch((err) => console.log(err));
};
Everything should be good with these suggestions, hope it works out...
Thank you all for the hints, the solution turned out to be much simpler, just use the fs.unlinkSync method instead of the asynchronous fs.unlink.
const deleteFile = (file) => {
try {
fs.unlinkSync(`./screenshots/${file}`);
console.log(`${file} removed`);
} catch (err) {
console.error(err);
}
};
const addFile = (file) => {
try {
zip.addLocalFile(`./screenshots/${file}`);
console.log(`${file} added`);
deleteFile(file);
} catch (err) {
console.error(err);
}
};
const makeZip = () => {
fs.readdirSync("./screenshots").map((file) => addFile(file));
zip.writeZip(`./zip_files/supername.zip`);
console.log("zip maked!");
};

Function constructor method

How can i use this method from my own project/design
Github.prototype.handleErr = function(res) {
if(!res.ok) {
throw new Error('Something went wrong!!' + res.status);
}
return res;
}
Inside another method but the same object
const git = new Github();
Github.prototype.getUser = (user) => {
return new Promise((resolve, reject) => {
// Profile
fetch(something)
.then(this.handleErr)
.then(response => response.json())
.then(data => resolve(data))
.catch(err => reject(err));
})
}
it didn't work with this.handleErr
i wanted to make a fetch request and use the git.handleErr() function inside .then() consumer, before handling the data response.
Ok so i figured out. I modified the arrow function syntax
Github.prototype.getUser = (user) => {}
to the normal syntax
Github.prototype.getUser = function(user) {}
Acording to this it was referred as a global/undefined this. so that's why it didn't work in the first instance. Thank you str.

How to handle an array of Promises then/catch

I don't know to to resolve this situation using JS promises.
Imagine I have some articles and I want to send a patch request to update them. I send one request per article. If the request of one article success then I update that article, but if the request fails, then I update the article differently. Also I want to show a message to the user informing if all the articles have been updated correctly or not.
This is not my real scenario and this may be a weird example. But it's what I want to accomplish in my React app.
Here is what I'm trying to do right now:
const saveArticles = articles => {
const promises = [];
articles.forEach(article => {
const promise = axios
.patch('/articles', article)
.then(() => updateArticleUi(article))
.catch(() => updateArticleUiWithError(article));
promises.push(promise);
});
Promise.all(promises)
.then(() => tellTheUserThereWasNoErrors())
.catch(() => tellTheUserThereWasSomeErrors());
};
This isn't working because the Promise.all is always executing the then callback, weather all promises succeed or not.
Thanks!
Your updateArticleUi and updateArticleUiWithErrors should have the respective return values so that you can distinguish whether there was an error or not when looking at the array of results:
function updateArticleUi(article) {
…
return {success: true};
}
function updateArticleUiWithError(article, error) {
…
return {success: false};
}
function saveArticles(articles) {
const promises = articles.map(article => {
return axios.patch('/articles', article).then(() =>
updateArticleUi(article)
, err =>
updateArticleUiWithError(article, err)
);
});
return Promise.all(promises).then(results =>
if (results.every(res => res.success)) {
tellTheUserThereWasNoErrors();
} else {
tellTheUserThereWasSomeErrors();
}
});
}
This works:
function axiosPatchRequest(url,data) {
return new Promise(function (resolve, reject) {
if (data) {
resolve(url);
} else {
reject('DATA_NOT_FOUND');
}
})
}
function updateArticleUi(data,article) {
return new Promise(function (resolve, reject) {
resolve({type:"updateArticleUi ",data,article});
})
}
function updateArticleUiWithError(data,article) {
return new Promise(function (resolve, reject) {
reject({type:"updateArticleUiWithError ",data,article});
})
}
function tellTheUserThereWasNoErrors(data){
console.log("tellTheUserThereWasNoErrors",data);
}
function tellTheUserThereWasSomeErrors(error){
console.log("tellTheUserThereWasSomeErrors",error);
}
const execute = (articles)=>{
const promises = [];
articles.forEach(article => {
const promise = axiosPatchRequest('/articles', article)
.then((data) => {
return updateArticleUi(data,article);
})
.catch((error) => {
return updateArticleUiWithError(error,article);
});
promises.push(promise);
});
Promise.all(promises)
.then((data) => tellTheUserThereWasNoErrors(data))
.catch((error) => tellTheUserThereWasSomeErrors(error));
};
execute(["one","","three"]);
execute(["one","two","three"]);
Promise.all will always call then because the promise you add to promises is already resolved one with either then or catch attached above
if you want to see catch to happen in .all chain just throw some exeption in updateArticleUiWithErro function
The problem is that you are catching the error thrown by
const promise = axios
.patch('/articles', article)
.then(() => updateArticleUi(article))
.catch(() => updateArticleUiWithError(article));
you should either throw a new error in the axios catch() or let the error bubble up by removing the catch from the axios call.

Extract .then and .catch from promises

I have written a general .js crud object which holds all the methods used to communicate with the server. However I'd like to avoid the repetition of .then and .catch and I would like to abstract that functionality in an external method.
Not sure if what I'm trying to achieve is even possible.
My code below:
all(url, success, fail){
return new Promise((resolve,reject) => {
_get(url)
.then((response) => {
if (response.status == 200) {
success.call(this,response);
return resolve();
}
})
.catch((error) => {
fail.call(this, error);
reject(error);
});
});}, submit, update .....
Wonderland desired result:
all(url, success, fail){
return new Promise((resolve, reject) => {
_get(url).handle(args);
});
}
Just avoid the Promise constructor antipattern and callbacks, and you'll be good!
function all(url) {
return _get(url).then((response) => {
if (response.status == 200) {
return response;
}
// most likely you want to `throw` an error here?
});
}

How to flatten a Promise within a Promise?

I have the following 2 functions, each returns a Promise:
const getToken = () => {
return new Promise((resolve, reject) => {
fs.readFile('token.txt', (err, data) => {
if (err) { return reject(err) }
if (!tokenIsExpired(data.token)) {
return resolve(data.token)
} else {
return requestNewToken()
}
})
})
}
const requestNewToken = () => {
return new Promise((resolve, reject) => {
restClient.get(url, (data, res) => {
fs.writeFile('tokenfile.txt', data.token, err => {
resolve(data.token)
})
})
})
}
function1()
.then(value => {
console.log('taco')
})
.catch(err => {
console.log(err)
})
So function1 runs, and (depending on some condition), it sometimes returns function2, which is returning another Promise. In this code, when function2 is called, the console.log('taco') never runs. Why is this? I thought that if you return a Promise from within a Promise, the resolved value of the nested Promise is what is resolved at the top level.
In order for me to get this to work, I have to do this:
const getToken = () => {
return new Promise((resolve, reject) => {
if (!tokenIsExpired()) {
return resolve(getToken())
} else {
return requestNewToken ()
.then(value => {
resolve(value)
})
}
})
}
That works, but it seems like I'm doing something wrong. It seems like there should be a more elegant way to handle/structure this.
You're right that promises auto-unwrap, but in this case you're returning from inside a promise constructor, which is ignored, you need to invoke either resolve or reject instead of using return. I think this might be the solution you're looking for:
const function1 = () => {
return new Promise((resolve, reject) => {
if (someCondition) {
resolve('foobar')
} else {
resolve(function2());
}
})
}
Inside a promise constructor, you need to call resolve or reject, which are equivalent to using return or throw from inside a then callback.
If you find this distinction confusing (I do), you should avoid the promise constructor entirely by just beginning a new chain with Promise.resolve, like this:
const function1 = () => {
return Promise.resolve().then(() => {
if (someCondition) {
return 'foobar';
} else {
return function2();
}
})
}
const function2 = () => {
return new Promise((resolve, reject) => {
resolve('hello world')
})
}
someCondition = false;
function1()
.then(value => {
console.log(value)
})
With your updated code, I recommend using a library to wrap APIs, rather than accessing the promise constructor yourself. For example, using bluebird's promisify:
const bluebird = require('bluebird');
const readFile = bluebird.promisify(fs.readFile);
const writeFile = bluebird.promisify(fs.writeFile);
const getUrl = bluebird.promisify(restClient.get, {multiArgs:true});
const getToken = () => {
return readFile('token.txt')
.then((data) => {
if(!tokenIsExpired(data.token)) {
return data.token;
} else {
return requestNewToken();
}
});
};
const requestNewToken = () => {
return getUrl(url)
.then(([data, res]) => {
return writeFile('tokenFile.txt', data.token)
.then(() => data.token);
});
};
I've remained faithful to your source code, but I'll note there may be a bug to do with writing data.token, and later trying to read the token property in that file.
Why use a library instead of the Promise constructor?
It allows you to write code which deals only with promises, which is (hopefully) easier to understand
You can be confident that callback APIs are correctly converted without losing errors. For example, if your tokenIsExpired function throws an error, using your promise constructor code, it would be lost. You would need to wrap all of your inner callback code in try {} catch(e) {reject(e)}, which is a hassle and easily forgotten.

Categories

Resources