I'm trying to convert my existing code using BlueBird, please suggest a best option to chain multiple request. Error happening in each callback needs to be redirected to rendered with different error.
request(option1, function (error, response, body) {
if (!error && response.statusCode == 200) {
var data= JSON.parse(body);
if(data.valid){
if(data.expired){
next();
} else {
request(option2, function (error2, response2, body2) {
var data2= JSON.parse(body2);
if(data2.valid) {
request(option3, function (error3, response3, body3) {
next();
})
} else {
res.json({error:'Error1'});
}
})
}
} else {
res.json({error:'Error2'});
}
} else {
res.json({error:'Error3'});
}
})
This is pretty straightforward, also note your current code doesn't handle errors in the second and third requests and this does:
var request = require("request-promise"); // request - converted to bluebird
request(option1).then(data=> {
if(!data.valid) throw Error("Error3");
if(data.expired) return;
return request(option2).then(JSON.parse);
}).then(data2 => {
if(!data2) return; // didn't need to fetch additional data
if(!data2.valid) throw Error("Error2");
return request(option3);
}).then(() => {
next();
}, e => {
res.json(error: e.message);
// better log this.
});
var rp = require('request-promise');
function handleError(err) {
res.json({
error: err.message
});
}
function parse(data) {
if (data) {
return JSON.parse(data);
}
}
rp(option1)
.then(parse)
.then(function (data) {
if (!data || !data.valid) {
throw Error('Error2');
}
if (data.expired) {
return;
}
return option2;
})
.then(rp)
.then(parse)
.then(function (data2) {
if (!data2 || !data2.valid) {
throw Error('Error1');
}
return option3;
})
.then(rp)
.then(parse)
.then(function () {
next();
})
.catch(handleError);
You don't need to manually check for statusCode but if you need to do so, first you have to add resolveWithFullResponse attribute to your option1 object, which allows you to receive the response object:
function checkStatusCode(response) {
if (response.statusCode !== 200) {
throw Error('Error3');
}
return response.body;
}
// add resolveWithFullResponse attribute to option1
option1.resolveWithFullResponse = true;
rp(option1)
.then(checkStatusCode)
.then(parse)
//...
Related
This question already has answers here:
Chained promises not passing on rejection
(4 answers)
Closed 3 years ago.
I want to return a fetch promise to upper layer to use, but I found even this fetch promise fail (be catched), upper layer's then still be called. Is it possible "upper layer's then" only be called when fetch success?
export default function request(url, options) {
.......
return fetch(url, options)
.then(checkStatus)
.then(parseJSON)
.then(data => {
// debugPrint("receive response" + JSON.stringify(data));
if (+data.status.code !== 200) {
message.error(data.status.message || "please retry");
}
return data;
})
.catch(err => {
message.error(err.toString() || "please retry");
return err;
});
}
// then I call request this way:
export function userLogin(account) {
return request(`${domain}/signin/get_accesstoken`, {
method: "POST"
}).then(data => {
// even catch be called, this then still be called. Is it possible only call this then when catch not be called ?
do something;
return data;
});
}
Second edit:
I try to return a promise in then, but look like it is not a promise be returned, I don't know why.
export default function request(url, options) {
.......
return fetch(url, options)
.then(checkStatus)
.then(parseJSON)
.then(data => {
// debugPrint("receive response" + JSON.stringify(data));
if (+data.status.code !== 200) {
message.error(data.status.message || "please retry");
}
return new Promise(resolve=>{
resolve(data)
});
})
.catch(err => {
message.error(err.toString() || "please retry");
return new Promise((resolve, reject)=>{
reject(err)
});
});
}
Edit third:
export default function request(url, options) {
.......
return fetch(url, options)
.then(checkStatus)
.then(parseJSON)
.then(data => {
// debugPrint("receive response" + JSON.stringify(data));
if (+data.status.code !== 200) {
message.error(data.status.message || "please retry");
}
return data;
})
.catch(err => {
message.error(err.toString() || "please retry");
return;
});
}
// then I call request this way:
export function userLogin(account) {
return request(`${domain}/signin/get_accesstoken`, {
method: "POST"
}).then(data => {
// add this
if (!data) {
return
}
do something;
return data;
});
}
if you want to call your upper layer's then only in case of success then throw some error in catch block of fetch instead of returning err.
export default function request(url, options) {
.......
return fetch(url, options)
.then(checkStatus)
.then(parseJSON)
.then(data => {
// debugPrint("receive response" + JSON.stringify(data));
if (+data.status.code !== 200) {
message.error(data.status.message || "please retry");
}
return data;
})
.catch(err => {
message.error(err.toString() || "please retry");
throw new Error('fetch failed'); // throw error
});
}
Recently started thinking that it was time to do a massive update to my logical operations, and part of that is the proper chaining of Angular JS asynchronous promise calls. Given the following code, how would I re-write it to be a proper chaining of two separate methods? (Yes, I've seen other posts about this, but they all deal with other versions of Angular, or other syntaxes, and I'm looking for something more up-to-date.)
vm.functionName = (
function() {
vm.processing = true;
api.promise1({ Id: vm.Id })
.then(
function(result) {
if (result.error) {
notificationService.danger("<h5>An error occurred.</h5><h6>Details: {0}</h6>".format(result.error));
} else {
api.promise2({ param: vm.param })
.then(
function(result2) {
if (result2.error) {
notificationService.danger("<h5>An error occurred.</h5><h6>Details: {0}</h6>".format(result2.error));
} else {
vm.data = result2.data;
notificationService.success("<h5>Operation successful!.</h5>");
}
vm.processing = false;
}
)
.catch(
function (err) {
console.error(err);
notificationService.danger("<h5>An error occurred.</h5><h6>Details: {0}</h6>".format(err.statusText));
vm.processing = false;
}
);
}
}
)
.catch(
function (err) {
console.error(err);
notificationService.danger("<h5>An error occurred.</h5><h6>Details: {0}</h6>".format(err.statusText));
vm.processing = false;
}
);
}
);
Logically, my brain tells me that I should be able to do something like this:
vm.functionName = (
function() {
vm.processing = true;
vm.promise1()
.then(
vm.promise2()
.then(
notificationService.success("<h5>Operation successful!.</h5>");
vm.processing = false;
);
);
);
}
);
vm.promise1 = (
function() {
api.promise1({ Id: vm.Id })
.then(
function(result) {
if (result.error) {
notificationService.danger("<h5>An error occurred.</h5><h6>Details: {0}</h6>".format(result.error));
}
}
)
.catch(
function (err) {
console.error(err);
notificationService.danger("<h5>An error occurred.</h5><h6>Details: {0}</h6>".format(err.statusText));
}
);
}
);
vm.promise2 = (
function() {
api.promise2({ param: vm.param })
.then(
function(result) {
if (result.error) {
notificationService.danger("<h5>An error occurred.</h5><h6>Details: {0}</h6>".format(result.error));
} else {
vm.data = result2.data;
}
}
)
.catch(
function (err) {
console.error(err);
notificationService.danger("<h5>An error occurred.</h5><h6>Details: {0}</h6>".format(err.statusText));
}
);
}
);
Update:
the "api...." calls above call to my service.js layer, where methods exist like such:
promise1: function (params, error) {
return $http
.post("/C#Controller/Method1", params)
.then(handleSuccess)
.catch(function (e) {
handleError(e, error);
});
},
promise2: function (params, error) {
return $http
.post("/C#Controller/Method2", params)
.then(handleSuccess)
.catch(function (e) {
handleError(e, error);
});
},
Updated, per Pop-A-Stash's ideas, as now implemented:
//#region Api Calls and Helper
function apiCallOne() {
return api.promise1({ Id: vm.Id });
}
function apiCallTwo() {
return api.promise2({param: vm.param });
}
function handleApiCallError(resultOrError, ngModelToSet) {
var errMsg = resultOrError.statusText === undefined ? resultOrError.error === undefined ? "Unknown Error" : resultOrError.error : resultOrError.statusText;
notificationService.danger("<h5>An error occurred.</h5><h6>Details: {0}</h6>".format(errMsg));
//This allows updating things like variables and ng-model goodies, via an inset function.
if (ngModelToSet) {
ngModelToSet();
}
}
//#endregion
//#region Initialization
function init() {
vm.pgLoaded = false;
apiCallOne()
.then(
function(result) {
if (!result.error) {
vm.data = result.data;
vm.pgLoaded = true;
} else {
handleApiCallError(result, function() { vm.pgLoaded = true; });
}
}
)
.catch(function(errorOne) { handleApiCallError(errorOne, function() { vm.pgLoaded = true; }); });
}
init();
//#endregion
You could shorten your code significantly using recursion to call the next promise in an array of objects containing promises and their parameters using something similar to this:
function runPromises(promises) {
var first = promises.shift();
first.function(first.params).then(function(resp) {
if (promises.length > 1) {
runPromises(promises);
}
}).catch(function (error) {
handleError(error);
});
}
with an initial array of something like this:
var promises = [
{
function: promise1,
params: any
},
{
function: promise2,
params: any
}
];
If each promise response requires individual handling you could add a callback to be fired after the promise is resolved for each promise.
If you want to chain them in a specific order, then you are already doing it correctly. However I see some code duplication that could be cleaned up:
vm.apiCallOne = apiCallOne;
vm.apiCallTwo = apiCallTwo;
vm.runChainedCalls = runChainedCalls;
function runChainedCalls() {
vm.processing = true;
vm.apiCallOne()
.then(function(result1) {
if(!result1.error) {
vm.apiCallTwo().then(function(result2) {
if(!result2.error) {
notificationService.success("<h5>Operation successful!.</h5>");
vm.data = result2.data;
vm.processing = false;
}
else {
handleError(result2);
}
})
.catch(function(errorTwo) {
handleError(errorTwo)
});
}
else {
handleError(result1);
}
})
.catch(function(errorOne) {
handleError(errorOne);
});
}
function apiCallOne(){
return api.callOne(param); //don't forget to return the promise object
};
function apiCallTwo() {
return api.callTwo(param); //don't forget to return the promise object
};
function handleError(resultOrError) {
notificationService.danger("<h5>An error occurred.</h5><h6>Details: {0}</h6>".format(resultOrError.statusText));
vm.processing = false;
}
You only need one .then() and .catch() for each call in your controller. Anymore than that is code duplication.
If you want to run them concurrently and don't care about order, you would use the $q.all() function to run them at the same time:
function runConcurrentCalls() {
$q.all([api.callOne(param), api.callTwo(param)]).then(function(responseArray) {
// responseArray contains array of both call responses
console.log(responseArray[0]);
console.log(responseArray[1]);
})
.catch(function() {
//something went wrong with one or both calls
});
}
My function looks like this now:
var GetImages = async() => {
var images_array = [];
await request ({
url: `https://api.tumblr.com/v2/blog/nameblog/posts?api_key=${process.env.TUMBLR_KEY}&type=photo`,
json: true
}, (error, response, body) => {
if(error){
console.log('Unable to connect');
}else if(body.meta.status === "ZERO_RESULTS"){
console.log('Uable to find that address.');
}else if(body.meta.status === 200){
body.response.posts.forEach(function(obj) {
obj.photos.forEach(function(photo) {
if(photo.original_size.width>photo.original_size.height){
images_array.push(photo.original_size.url);
console.log("dawdaw");
}
});
});
//callback(images_array);
}
});
return images_array;
}
I have no idea, how return my array after i'll fill it with values. With callback it works fine, but i wanna do it with async/await methid in right way. Thank you for help.
create method to return promise for request and use that method with await
requestPromise = () => {
return new Promise(function(resolve, reject) {
request({
url: `https://api.tumblr.com/v2/blog/nameblog/posts?api_key=${process.env.TUMBLR_KEY}&type=photo`,
json: true
}, (error, response, body) => {
if (error) {
console.log('Unable to connect');
reject();
} else if (body.meta.status === "ZERO_RESULTS") {
console.log('Uable to find that address.');
reject();
} else if (body.meta.status === 200) {
body.response.posts.forEach(function(obj) {
obj.photos.forEach(function(photo) {
if (photo.original_size.width > photo.original_size.height) {
images_array.push(photo.original_size.url);
console.log("dawdaw");
}
});
});
resolve(images_array)
}
});
});
}
var GetImages = async() => {
try
{
images = await requestPromise();
return images;
}
catch(e){return [];}
}
I have duplicated error handling code in my Node.js code, how can I make it better to get rid of duplicated code. I specifically want to ask error handling about this callback way, not the Promise way.
var request = require('request');
var URL = 'http://localhost:3000';
var getRanking = function get_rank(error, response, body) {
if (error) {
handleError(error);
} else {
if (response.statusCode != 200) {
handleError(response);
} else {
console.log('Response 1 ' + body);
request(URL + '/iso/country/' + JSON.parse(body).Country, getISO);
}
}
}
var getISO = function get_iso(error, response, body) {
if (error) {
handleError(error);
} else {
if (response.statusCode != 200) {
handleError(response)
} else {
console.log("Response 2 "+body);
request(URL+'/olympic/2016/medal/'+JSON.parse(body).iso,getMedalCount);
}
}
}
var getMedalCount = function get_medal_count(error, response, body) {
if (error) {
handleError(error);
} else {
if (response.statusCode != 200) {
handleError(response);
} else {
console.log("Response 3 " + body);
}
}
}
function handleError(err) {
console.log('Error ' + JSON.stringify(err))
}
request(URL+'/olympic/2016/ranking/4', getRanking);
Create a funciton handleResponse and write the response handling duplicated code in that funciton.
Call that function with required parameters as given,
var request = require('request');
var URL = 'http://localhost:3000';
var getRanking = function get_rank(error, response, body) {
handleResponse(error, response, body, 'getISO');
}
var getISO = function get_iso(error, response, body) {
handleResponse(error, response, body, 'getMedalCount');
}
var getMedalCount = function get_medal_count(error, response, body) {
handleResponse(error, response, body null);
}
function handleResponse(error, response, body, url) {
if (error) {
handleError(error);
} else {
if (response.statusCode != 200) {
handleError(response);
} else {
if(url == 'getISO')
{
request(URL + '/iso/country/' + JSON.parse(body).Country, getISO);
}
else if(url == 'getMedalCount')
{
request(URL+'/olympic/2016/medal/'+JSON.parse(body).iso,getMedalCount);
}
}
}
}
function handleError(err) {
console.log('Error ' + JSON.stringify(err))
}
request(URL+'/olympic/2016/ranking/4', getRanking);
You can try following code:
var request = require('request');
var URL = 'http://localhost:3000';
var getRanking = function get_rank(error, response, body) {
if (error) {
throw error;
}
if (response.statusCode != 200) {
throw new Error(response);
}
console.log('Response 1 ' + body);
request(URL + '/iso/country/' + JSON.parse(body).Country, getISO);
}
var getISO = function get_iso(error, response, body) {
if (error) {
throw error;
}
if (response.statusCode != 200) {
throw new Error(response);
}
console.log("Response 2 "+body);
request(URL+'/olympic/2016/medal/'+JSON.parse(body).iso,getMedalCount);
}
}
var getMedalCount = function get_medal_count(error, response, body) {
if (error) {
throw error;
}
if (response.statusCode != 200) {
throw new Error(response);
return;
}
console.log("Response 3 " + body);
}
try {
request(URL+'/olympic/2016/ranking/4', getRanking);
} catch(ex) {
console.log(ex);
}
Ok, as far as noone suggested such optimization, I will.
Instead of such block:
if (error) {
handleError(error);
} else {
if (response.statusCode != 200) {
handleError(response);
} else {
console.log("Response 3 " + body);
}
}
You can do this way:
if (error || response.statusCode != 200) handlerError(error || response);
else console.log("Response 3 " + body);
You can use following code to handle response.
var request = require('request');
var URL = "http://localhost:3000";
var getRanking = function get_rank (body) {
console.log("Response 1 " + body);
request(URL + '/iso/country/' + JSON.parse(body).Country, handleResponse.bind(null, getISO));
}
var getISO = function get_iso (body) {
console.log("Response 2 " + body);
request(URL + '/olympic/2016/medal/' + JSON.parse(body).iso, handleResponse.bind(null, getMedalCount));
}
var getMedalCount = function get_medal_count (body) {
console.log("Response 3 " + body);
}
function handleResponse (callback, error, response, body) {
console.log(error, response, body, callback)
if (error || response.statusCode != 200) {
console.log('Error ' + JSON.stringify(error))
}
else {
callback(body);
}
}
request(URL + '/olympic/2016/ranking/4', handleResponse.bind(null, getRanking));
The simpliest way to shorten your code would probably be the following :
function handleError(err, res) {
if(err){
console.log('Error '+JSON.stringify(err));
return false;
} else if(res.statusCode != 200) {
console.log('Error '+JSON.stringify(res));
return false;
} else {
return true;
}
}
// and then, use it in your functions like that :
var getRanking = function get_rank(error,response,body) {
if (handleError(err, res)) { // if there is no error, do your stuff
console.log("Response 1 "+body);
request(URL+'/iso/country/'+JSON.parse(body).Country,getISO);
}
}
But I think that's not very appropriate to JS, because JS can be used as a functional programming language (which is better) and this approach looks more procedural (like C language).
So, in my opinion, the below solution would be proper :
function handleError(successFunc) {
return function (error, response, body) {
if (error) {
throw new Error(error);
} else if(response.statusCode != 200) {
throw new Error(response);
} else {
successFunc(response);
}
}
}
// then, just pass your successFunc to your error handler :
var getRanking = function (response) {
// do your success stuff.
}
try {
request(URL+'/olympic/2016/ranking/4', handleError(getRanking));
} catch (e) {
// catch your error here
}
If your request is a success, getRanking will be executed, if this is a failure, the error will be logged and thrown.
With this solution, you can pass any function to your error handler and it will be used if the request succeed, if the request fails, the error handler will throw an error and then, you will be able to catch it.
Hope it helps,
Best regards
I currently have working code that does a request and checks if it receives a successful status code of 200. I would like to grow on this and loop it where it will keep sending requests until the status code is 200. I tried using a while loop but was not receiving the correct results. Thanks for the help!
request('http://0.0.0.0:9200', function (error, response, body) {
if (!error && response.statusCode == 200) {
console.log('success');
do(something);
}
else {
console.log('fail');
}
});
Would be something like:
let retry = (function() {
let count = 0;
return function(max, timeout, next) {
request('http://0.0.0.0:9200', function (error, response, body) {
if (error || response.statusCode !== 200) {
console.log('fail');
if (count++ < max) {
return setTimeout(function() {
retry(max, timeout, next);
}, timeout);
} else {
return next(new Error('max retries reached'));
}
}
console.log('success');
next(null, body);
});
}
})();
retry(20, 1000, function(err, body) {
do(something);
});
You can set a max number of retries and a timeout between retries. So that you do not introduce an infinite loop, and you do not deliver the final punch to an overloaded request target ^^
I wanted a little more intuitive answer including promises. I build on top of miggs answer within a try/catch the code below with promises and axios.
Based on a simple example of recursive functions
const throwNumbers = (count = 0) => {
console.log(count);
if (count++ < 10) {
throwNumbers(count);
} else {
console.log('max reached');
};
};
You can put anything else on the try part and handle error codes in the catch part. You have to set a max number of retries, which is 10 in my case.
let getResponse = async(count = 0) => {
try {
const axiosResponse = await axios.get(someURL, {
params: {
parameter1: parameter1,
},
});
return axiosResponse;
} catch (error) {
if (error || error.status != 200) {
console.error('failed, retry');
if (count++ < 10) {
return getResponse(count);
} else {
throw new Error('max retries reached');
};
} else {
throw error;
};
};
};
You would call the function with the following and handle the body or whatever with the response value.
let response = await getResponse();
console.log('This is the response:', response);
Has no timeout but works for me.