How to attempt an async function multiple times using promise, .then(), .catch() - javascript

I need to do several attempts on the async function getDBfileXHR, but I don't know how to handle this. I am new to chaining promises. Should I chain them like this?
return getDBfileXHR(dbUrl(), serverAttempts)
.then(function () { // success
console.log('-basic XHR request succeeded.');
return dbReadyDeferred.promise;
})
.catch(function (){
return getDBfileXHR(dbUrl(), serverAttempts)
.then(function (){
console.log('-basic XHR request succeeded after second attempt.');
return dbReadyDeferred.promise;
})
.catch(function () { // error
console.log("-basic XHR request failed, falling back to local DB file or localStorage DB...");
return fallbackToLocalDBfileOrLocalStorageDB();
});
})
or like that :
return getDBfileXHR(dbUrl(), serverAttempts)
.then(function () { // success
console.log('-basic XHR request succeeded.');
return dbReadyDeferred.promise;
})
.catch(function (){
if (typeof serverAttempts !== "undefined") serverAttempts++;
console.log('on passe dans le catch, serverAttempts = ', serverAttempts)
if (serverAttempts < 2) {
return getDBfileXHR(dbUrl(), serverAttempts)
.then(function () { // success
console.log('-basic XHR request succeeded.');
return dbReadyDeferred.promise;
})
.catch(function (){
console.log("-basic XHR request failed, falling back to local DB file or localStorage DB...");
return fallbackToLocalDBfileOrLocalStorageDB();
})
} else {
console.log("-basic XHR request failed, falling back to local DB file or localStorage DB...");
return fallbackToLocalDBfileOrLocalStorageDB();
}
})
This second code seems to work, but I am not sure it is best practices.

A simple and flexible solution involves creating a helper - benefit, reusable for anything that requires retrying promises:
var retryP = (fn, retry) => fn(retry).catch(err => (!isNaN(retry) && retry > 0) ? retryP(fn, retry - 1) : Promise.reject(err));
This generic function will retry fn for at most attempts number of times, passing 1 will retry once, i.e. make two attempts
your function can then be written:
var serverAttempts = 1;
// this is should be the retry attempts,
// so 0 is try at most once, 1 is at most twice etc
// argument n will be the number of retries "in hand",
// so it counts down from the passed in value to 0
return retryP(n => getDBfileXHR(dbUrl(), serverAttempts - n), serverAttempts)
.then(() => {
console.log('-basic XHR request succeeded after second attempt.');
return dbReadyDeferred.promise;
})
.catch(() => {
console.log("-basic XHR request failed, falling back to local DB file or localStorage DB...");
return fallbackToLocalDBfileOrLocalStorageDB();
});
If you aren't comfortable with ES2015+ syntax
helper:
var retryP = function retryP(fn, retry) {
return fn(retry).catch(function (err) {
return !isNaN(retry) && retry > 0 ? retryP(fn, retry - 1) : Promise.reject(err);
});
};
code:
var serverAttempts = 1;
return retryP(function (n) {
return getDBfileXHR(dbUrl(), serverAttempts - n);
}, serverAttempts)
.then(function () {
console.log('-basic XHR request succeeded after second attempt.');
return dbReadyDeferred.promise;
})
.catch(function () {
console.log("-basic XHR request failed, falling back to local DB file or localStorage DB...");
return fallbackToLocalDBfileOrLocalStorageDB();
});

Related

Dojo deferred retry until success

I have a method in my dojo class which makes a request (say, a JSON one). If it succeeds, well and good. However, if it fails (times out or whatever), I want it to try again until it succeeds. To do this, I call the method itself in the error callback:
doReq: function(){
var req = Request(...);
return req.then(function(response, io){
// Success!
}, dojo.hitch(this, function(error, io){
this.doReq(); // Failed; try again.
}));
}
Am I doing this correctly?
It can be done this way, but you may want to limit attempts,
for example:
doReq: function(attempts){
attempts -= 1;
var req = Request(...);
return req.then(function(response, io){
// Success!
}, dojo.hitch(this, function(error, io){
if (attempts > 0) this.doReq(attempts); // Failed; try again.
else //return some error here
}));
}
I'm not sure why you return req.then(...), this will return new promise not the req's promise.
But if you want the caller of doReq to get response when the req succeeds, you can do it like this.
_request: function (deferred) {
var req = Request(...);
req.then(dojo.hitch(this, function (response, io) {
// Success!
deferred.resolve(response);
}), dojo.hitch(this, function (error, io) {
this._request(deferred); // Failed; try again.
// use deferred.reject('some error message'); to reject when it reached the retry limit, if you want to.
}));
},
doReq: function () {
var deferred = new Deferred(); // from module "dojo/Deferred"
this._request(deferred);
return deferred.promise;
}
This is how to use it.
var thePromise = this.doReq();
thePromise.then(dojo.hitch(this, function (response) {
console.log('response: ', response); // your response from deferred.resolve(response);
}), dojo.hitch(this, function (error) {
console.log('error: ', error); // your error from deferred.reject('some error message'); if you have.
}));

Is there some way to make recursive fetch requests?

I want my fetch request to have some sort of retry system if it somehows fails based on the HTTP code of the response (for example: not 200). It looks something like this:
fetch('someURLWithAJSONfile/file.json')
.then(function (res) {
console.log(res.status);
if (res.status !== 200) {
console.log("There was an error processing your fetch request. We are trying again.");
// Recursive call to same fetch request until succeeds
} else {
return res.json();
}
}).then(function (json) {
data = json;
}).catch(function (err) {
console.log(`There was a problem with the fetch operation: ${err.message}`);
});
Is there a way to put the fetch request inside a custom Promise and make it call itself after checking its http response status?
Here the simple ES6 solution (since you are using fetch). The limit option means how many times you want to try your request.
var doRecursiveRequest = (url, limit = Number.MAX_VALUE) =>
fetch(url).then(res => {
if (res.status !== 200 && --limit) {
return doRecursiveRequest(url, limit);
}
return res.json();
});
doRecursiveRequest('someURLWithAJSONfile/file.json', 10)
.then(data => console.log(data))
.catch(error => console.log(error));
You can do this by wrapping your call to fetch in a named function that returns the promise created by fetch. Consider:
function fetchWithRetry(url, retryLimit, retryCount) {
retryLimit = retryLimit || Number.MAX_VALUE;
retryCount = Math.max(retryCount || 0, 0);
return fetch(url).then(function (res) {
console.log(res.status);
if (res.status !== 200 && retryCount < retryLimit) {
console.log("There was an error processing your fetch request. We are trying again.");
return fetchWithRetry(url, retryLimit, retryCount + 1);
} else {
return res.json();
}
});
}
fetchWithRetry('someURLWithAJSONfile/file.json', 10).then(function (json) {
data = json;
}).catch(function (err) {
console.log(`There was a problem with the fetch operation: ${err.message}`);
});
This code wraps your existing call and takes advantage of closure scope to maintain a retry limit and count which are both optional. You then call the fetchWithRetry function with a URL just like you did your previous call to fetch. If you do not pass a retry limit it will continue endlessly. The final retryCount variable is really only used for recursion purposes and is meant to be called internally.

Trouble Setting Async Request Result

I'm having difficulty accessing the result of an asynchronous request to an rss feed. I referred to the post, How do I return the response from an asynchronous call?, which suggests using Promises. I implemented a Promise, which resolves, but still cannot access the result of the request outside the request function.
I am attempting to set the variable film to the request result, but can only seem to get undefined.
var request = require("request");
var parseString = require("xml2js").parseString;
var url = 'http://feeds.frogpants.com/filmsack_feed.xml';
var film;
request(url, function(error, response, body) {
return new Promise(function(resolve, reject) {
if (error) {
console.log(error);
}
else {
if (response.statusCode === 200) {
parseString(body, function(err, result) {
var feed = result.rss.channel[0].item.reduce(function(acc, item) {
acc.push(item);
return acc;
}, [])
resolve(feed)
})
}
}
})
.then(function(data) {
film = data;
})
})
console.log(film)
Logging feed from within the request function returns the results that I am looking for. What am I missing? Thanks.
I'm not very experienced, but I think the issue is that console.log is executed before your request (asynchronous) returns a result.
Perhaps you should try returning "film" from the last then() and chain another then() to console.log that var
...
.then(function(data) {
return film = data;
})
.then((result) => console.log(result))
})

Node Js express async response with request and Redis call

So i'm having an issue with handling async actions in NodeJS while trying to send response to a request, with some async calls in the middle. (And to make this party even more complicated, i'm also want to use async.parallel )
Basically i'm trying to get value from Redis, and if he doesn't exist, get it from a provider (with request and response based using axios).
This is the code snippet :
this.getFixturesByTimeFrame = function (timeFrame, res) {
function getGamesData(timeFrame,finalCallback) {
var calls = [];
var readyList = [];
//Creating calls for parallel
timeFrame.forEach(function(startDay){
calls.push(function(callback) {
//Problematic async call
redisClient.get(startDay, function (error, exist) {
console.log('Got into the redis get!');
if (error){
console.log('Redis error : '+error);
}
if (exist) {
console.log('Date is in the cache! return it');
return exist;
} else {
//We need to fetch the data from the provider
console.log('Date is not in the cache, get it from the provider');
getFixturesDataFromProvider(startDay)
.then(organizeByLeagues)
.then(function (gamesForADay) {
redisClient.setex(startDay, 600, gamesForADay);
responsesList.add(gamesForADay);
callback(null, gamesForADay);
}).catch(function (response) {
if (response.status == 404) {
callback('Cant get games from provider');
}
});
}
});
}
)});
async.parallel(calls, function(err, responsesList) {
/* this code will run after all calls finished the job or
when any of the calls passes an error */
if (err){
res.send(501);
} else {
console.log('Here is the final call, return the list here');
//Some data manipulation here - just some list ordering and for each loops
console.log('finished listing, send the list');
finalCallback(responsesList);
}
});
}
getGamesData(timeFrame, function (readyList) {
res.send(readyList);
});
};
function getFixturesDataFromProvider(date) {
var requestUrl = 'someURL/ + date;
return axios.get(requestUrl, config);
}
function organizeByLeagues(matchDay) {
if (matchDay.count == 0) {
console.log('No games in this day from the provider');
return [];
} else {
var organizedSet = [];
//Some list manipulations using for each
return organizedSet;
}
}
But the response is been sent before parallel has been starting doing his things...
i'm missing something with the callbacks and the async calls for sure but i'm not sure where...
Thanks

Return 2 variables using bluebird promises

I am trying to write a promise function using Bluebird library for nodejs. I want to return 2 variables from my function.
I want the first function to return immediately and the second to complete its own promise chain before returning.
function mainfunction() {
return callHelperfunction()
.then(function (data) {
//do something with data
//send 200 Ok to user
})
.then(function (data2) {
//wait for response from startthisfunction here
})
.catch(function (err) {
//handle errors
});
}
function callHelperfunction() {
return anotherHelperFunction()
.then(function (data) {
return data;
return startthisfunction(data)
.then(function () {
//do something more!
})
});
}
Just like regular functions only have one return value, similarly promises only resolve with one value since it's the same analogy.
Just like with regular functions, you can return a composite value from a promise, you can also consume it using .spread for ease if you return an array:
Promise.resolve().then(function(el){
return [Promise.resolve(1), Promise.delay(1000).return(2));
}).spread(function(val1, val2){
// two values can be accessed here
console.log(val1, val2); // 1, 2
});
The only thing that appears to be wrong is the expectation that do something with data; send 200 Ok to user; should be performed in mainfunction(), part way through the promise chain in callHelperfunction().
This can be overcome in a number of ways. Here's a couple :
1. Move do something with data; send 200 Ok to user; into callHelperfunction()
function mainfunction() {
return callHelperfunction())
.catch(function (err) {
//handle errors
});
}
function callHelperfunction() {
return anotherHelperFunction()
.then(function (data1) {
//do something with data
//send 200 Ok to user
return startthisfunction(data1)
.then(function (data2) {
//wait for response from startthisfunction here
//do something more!
});
});
}
2. Dispense with callHelperfunction() altogether and do everything in mainfunction()
function mainfunction() {
return anotherHelperFunction()
.then(function (data1) {
//do something with data1
//send 200 Ok to user
return startthisfunction(data1);
})
.then(function (data2) {
//wait for response from startthisfunction here
})
.catch(function (err) {
//handle errors
});
}

Categories