When Response.text() promise will reject? - javascript

I see that on MDN/Response/text docs show the example of using .text() only with then
response.text().then(function (text) {
// do something with the text response
});
It returns a promise that resolves with a String.
Because of lint rules, I need to put
// eslint-disable-next-line #typescript-eslint/no-floating-promises
res.text().then(async (t) => {
Is there a use case when I need to catch a rejected promise from Response.text()? Maybe some examples?

It can fail/reject if the response was already consumed/read, so i.e if something already called .text/.json etc on it.
Looking at the polyfill implementation (https://github.com/github/fetch/blob/d1d09fb8039b4b8c7f2f5d6c844ea72d8a3cefe6/fetch.js#L301 ) I don't see other likely cases though.
Example:
response.text()
.then(t1 => {
console.log({ t1 }); // after calling text() we can see the result here
return response; // but we decided to return the response to the next handler
})
.then(res =>res.text()) // here we try to read text() again
.then(t2 => console.log({ t2 })) // and expecting text to be logged here
.catch(er => console.log({ er })); // but the text() promise rejects with
// TypeError: Failed to execute 'text' on 'Response': body stream already read

Related

Handle a reject in promise.all() in javascript [duplicate]

I have an array of Promises that I'm resolving with Promise.all(arrayOfPromises);
I go on to continue the promise chain. Looks something like this
existingPromiseChain = existingPromiseChain.then(function() {
var arrayOfPromises = state.routes.map(function(route){
return route.handler.promiseHandler();
});
return Promise.all(arrayOfPromises)
});
existingPromiseChain = existingPromiseChain.then(function(arrayResolved) {
// do stuff with my array of resolved promises, eventually ending with a res.send();
});
I want to add a catch statement to handle an individual promise in case it errors, but when I try, Promise.all returns the first error it finds (disregards the rest), and then I can't get the data from the rest of the promises in the array (that didn't error).
I've tried doing something like ..
existingPromiseChain = existingPromiseChain.then(function() {
var arrayOfPromises = state.routes.map(function(route){
return route.handler.promiseHandler()
.then(function(data) {
return data;
})
.catch(function(err) {
return err
});
});
return Promise.all(arrayOfPromises)
});
existingPromiseChain = existingPromiseChain.then(function(arrayResolved) {
// do stuff with my array of resolved promises, eventually ending with a res.send();
});
But that doesn't resolve.
Thanks!
--
Edit:
What the answers below said were completely true, the code was breaking due to other reasons. In case anyone is interested, this is the solution I ended up with ...
Node Express Server Chain
serverSidePromiseChain
.then(function(AppRouter) {
var arrayOfPromises = state.routes.map(function(route) {
return route.async();
});
Promise.all(arrayOfPromises)
.catch(function(err) {
// log that I have an error, return the entire array;
console.log('A promise failed to resolve', err);
return arrayOfPromises;
})
.then(function(arrayOfPromises) {
// full array of resolved promises;
})
};
API Call (route.async call)
return async()
.then(function(result) {
// dispatch a success
return result;
})
.catch(function(err) {
// dispatch a failure and throw error
throw err;
});
Putting the .catch for Promise.all before the .then seems to have served the purpose of catching any errors from the original promises, but then returning the entire array to the next .then
Thanks!
Promise.all is all or nothing. It resolves once all promises in the array resolve, or reject as soon as one of them rejects. In other words, it either resolves with an array of all resolved values, or rejects with a single error.
Some libraries have something called Promise.when, which I understand would instead wait for all promises in the array to either resolve or reject, but I'm not familiar with it, and it's not in ES6.
Your code
I agree with others here that your fix should work. It should resolve with an array that may contain a mix of successful values and errors objects. It's unusual to pass error objects in the success-path but assuming your code is expecting them, I see no problem with it.
The only reason I can think of why it would "not resolve" is that it's failing in code you're not showing us and the reason you're not seeing any error message about this is because this promise chain is not terminated with a final catch (as far as what you're showing us anyway).
I've taken the liberty of factoring out the "existing chain" from your example and terminating the chain with a catch. This may not be right for you, but for people reading this, it's important to always either return or terminate chains, or potential errors, even coding errors, will get hidden (which is what I suspect happened here):
Promise.all(state.routes.map(function(route) {
return route.handler.promiseHandler().catch(function(err) {
return err;
});
}))
.then(function(arrayOfValuesOrErrors) {
// handling of my array containing values and/or errors.
})
.catch(function(err) {
console.log(err.message); // some coding error in handling happened
});
NEW ANSWER
const results = await Promise.all(promises.map(p => p.catch(e => e)));
const validResults = results.filter(result => !(result instanceof Error));
FUTURE Promise API
Chrome 76: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled
You can download https://www.npmjs.com/package/promise.allsettled to get it now. In certain browsers allSettled comes preinstalled with the browser itself. It's worth downloading the package for peace of mind because eg. TypeScript doesn't have default definitions for allSettled.
ES2020 introduces new method for the Promise type: Promise.allSettled().
Promise.allSettled gives you a signal when all the input promises are settled, which means they’re either fulfilled or rejected. This is useful in cases where you don’t care about the state of the promise, you just want to know when the work is done, regardless of whether it was successful.
async function() {
const promises = [
fetch('/api.stackexchange.com/2.2'), // succeeds
fetch('/this-will-fail') // fails
];
const result = await Promise.allSettled(promises);
console.log(result.map(promise => promise.status));
// ['fulfilled', 'rejected']
}
Read more in the v8 blog post.
To continue the Promise.all loop (even when a Promise rejects) I wrote a utility function which is called executeAllPromises. This utility function returns an object with results and errors.
The idea is that all Promises you pass to executeAllPromises will be wrapped into a new Promise which will always resolve. The new Promise resolves with an array which has 2 spots. The first spot holds the resolving value (if any) and the second spot keeps the error (if the wrapped Promise rejects).
As a final step the executeAllPromises accumulates all values of the wrapped promises and returns the final object with an array for results and an array for errors.
Here is the code:
function executeAllPromises(promises) {
// Wrap all Promises in a Promise that will always "resolve"
var resolvingPromises = promises.map(function(promise) {
return new Promise(function(resolve) {
var payload = new Array(2);
promise.then(function(result) {
payload[0] = result;
})
.catch(function(error) {
payload[1] = error;
})
.then(function() {
/*
* The wrapped Promise returns an array:
* The first position in the array holds the result (if any)
* The second position in the array holds the error (if any)
*/
resolve(payload);
});
});
});
var errors = [];
var results = [];
// Execute all wrapped Promises
return Promise.all(resolvingPromises)
.then(function(items) {
items.forEach(function(payload) {
if (payload[1]) {
errors.push(payload[1]);
} else {
results.push(payload[0]);
}
});
return {
errors: errors,
results: results
};
});
}
var myPromises = [
Promise.resolve(1),
Promise.resolve(2),
Promise.reject(new Error('3')),
Promise.resolve(4),
Promise.reject(new Error('5'))
];
executeAllPromises(myPromises).then(function(items) {
// Result
var errors = items.errors.map(function(error) {
return error.message
}).join(',');
var results = items.results.join(',');
console.log(`Executed all ${myPromises.length} Promises:`);
console.log(`— ${items.results.length} Promises were successful: ${results}`);
console.log(`— ${items.errors.length} Promises failed: ${errors}`);
});
Promise.allSettled
Instead of Promise.all use Promise.allSettled which waits for all promises to settle, regardless of the result
let p1 = new Promise(resolve => resolve("result1"));
let p2 = new Promise( (resolve,reject) => reject('some troubles') );
let p3 = new Promise(resolve => resolve("result3"));
// It returns info about each promise status and value
Promise.allSettled([p1,p2,p3]).then(result=> console.log(result));
Polyfill
if (!Promise.allSettled) {
const rejectHandler = reason => ({ status: 'rejected', reason });
const resolveHandler = value => ({ status: 'fulfilled', value });
Promise.allSettled = function (promises) {
const convertedPromises = promises
.map(p => Promise.resolve(p).then(resolveHandler, rejectHandler));
return Promise.all(convertedPromises);
};
}
As #jib said,
Promise.all is all or nothing.
Though, you can control certain promises that are "allowed" to fail and we would like to proceed to .then.
For example.
Promise.all([
doMustAsyncTask1,
doMustAsyncTask2,
doOptionalAsyncTask
.catch(err => {
if( /* err non-critical */) {
return
}
// if critical then fail
throw err
})
])
.then(([ mustRes1, mustRes2, optionalRes ]) => {
// proceed to work with results
})
Using Async await -
here one async function func1 is returning a resolved value, and func2 is throwing a error and returning a null in this situation, we can handle it how we want and return accordingly.
const callingFunction = async () => {
const manyPromises = await Promise.all([func1(), func2()]);
console.log(manyPromises);
}
const func1 = async () => {
return 'func1'
}
const func2 = async () => {
try {
let x;
if (!x) throw "x value not present"
} catch(err) {
return null
}
}
callingFunction();
Output is - [ 'func1', null ]
if you get to use the q library https://github.com/kriskowal/q
it has q.allSettled() method that can solve this problem
you can handle every promise depending on its state either fullfiled or rejected
so
existingPromiseChain = existingPromiseChain.then(function() {
var arrayOfPromises = state.routes.map(function(route){
return route.handler.promiseHandler();
});
return q.allSettled(arrayOfPromises)
});
existingPromiseChain = existingPromiseChain.then(function(arrayResolved) {
//so here you have all your promises the fulfilled and the rejected ones
// you can check the state of each promise
arrayResolved.forEach(function(item){
if(item.state === 'fulfilled'){ // 'rejected' for rejected promises
//do somthing
} else {
// do something else
}
})
// do stuff with my array of resolved promises, eventually ending with a res.send();
});
For those using ES8 that stumble here, you can do something like the following, using async functions:
var arrayOfPromises = state.routes.map(async function(route){
try {
return await route.handler.promiseHandler();
} catch(e) {
// Do something to handle the error.
// Errored promises will return whatever you return here (undefined if you don't return anything).
}
});
var resolvedPromises = await Promise.all(arrayOfPromises);
Promise.allSettled with a filter
const promises = [
fetch('/api-call-1'),
fetch('/api-call-2'),
fetch('/api-call-3'),
];
// Imagine some of these requests fail, and some succeed.
const resultFilter = (result, error) => result.filter(i => i.status === (!error ? 'fulfilled' : 'rejected')).map(i => (!error ? i.value : i.reason));
const result = await Promise.allSettled(promises);
const fulfilled = resultFilter(result); // all fulfilled results
const rejected = resultFilter(result, true); // all rejected results
Have you considered Promise.prototype.finally()?
It seems to be designed to do exactly what you want - execute a function once all the promises have settled (resolved/rejected), regardless of some of the promises being rejected.
From the MDN documentation:
The finally() method can be useful if you want to do some processing or cleanup once the promise is settled, regardless of its outcome.
The finally() method is very similar to calling .then(onFinally, onFinally) however there are couple of differences:
When creating a function inline, you can pass it once, instead of being forced to either declare it twice, or create a variable for it.
A finally callback will not receive any argument, since there's no reliable means of determining if the promise was fulfilled or rejected. This use case is for precisely when you do not care about the rejection reason, or the fulfillment value, and so there's no need to provide it.
Unlike Promise.resolve(2).then(() => {}, () => {}) (which will be resolved with undefined), Promise.resolve(2).finally(() => {}) will be resolved with 2.
Similarly, unlike Promise.reject(3).then(() => {}, () => {}) (which will be fulfilled with undefined), Promise.reject(3).finally(() => {}) will be rejected with 3.
== Fallback ==
If your version of JavaScript doesn't support Promise.prototype.finally() you can use this workaround from Jake Archibald: Promise.all(promises.map(p => p.catch(() => undefined)));
We can handle the rejection at the individual promises level, so when we get the results in our result array, the array index which has been rejected will be undefined. We can handle that situation as needed, and use the remaining results.
Here I have rejected the first promise, so it comes as undefined, but we can use the result of the second promise, which is at index 1.
const manyPromises = Promise.all([func1(), func2()]).then(result => {
console.log(result[0]); // undefined
console.log(result[1]); // func2
});
function func1() {
return new Promise( (res, rej) => rej('func1')).catch(err => {
console.log('error handled', err);
});
}
function func2() {
return new Promise( (res, rej) => setTimeout(() => res('func2'), 500) );
}
const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'foo'));
const promises = [promise1, promise2];
let sum = 0;
let promiseErrorArr = [];
Promise.allSettled(promises)
.then((results) => {
results.forEach(result => {
if (result.status === "rejected") {
sum += 1;
promiseErrorArr.push(result)
}
})
return ( (sum>0) ? promiseFailed() : promisePassed())
})
function promiseFailed(){
console.log('one or all failed!')
console.log(promiseErrorArr)
}
function promisePassed(){
console.log('all passed!')
}
// expected output:
// "one or all failed!"
// Array [Object { status: "rejected", reason: "foo" }]
Alternately, if you have a case where you don't particularly care about the values of the resolved promises when there is one failure but you still want them to have run, you could do something like this which will resolve with the promises as normal when they all succeed and reject with the failed promises when any of them fail:
function promiseNoReallyAll (promises) {
return new Promise(
async (resolve, reject) => {
const failedPromises = []
const successfulPromises = await Promise.all(
promises.map(
promise => promise.catch(error => {
failedPromises.push(error)
})
)
)
if (failedPromises.length) {
reject(failedPromises)
} else {
resolve(successfulPromises)
}
}
)
}
You can always wrap your promise returning functions in a way that they catches failure and returning instead an agreed value (e.g. error.message), so the exception won't roll all the way up to the Promise.all function and disable it.
async function resetCache(ip) {
try {
const response = await axios.get(`http://${ip}/resetcache`);
return response;
}catch (e) {
return {status: 'failure', reason: 'e.message'};
}
}
I've found a way (workaround) to do this without making it sync.
So as it was mentioned before Promise.all is all of none.
so... Use an enclosing promise to catch and force resolve.
let safePromises = originalPrmises.map((imageObject) => {
return new Promise((resolve) => {
// Do something error friendly
promise.then(_res => resolve(res)).catch(_err => resolve(err))
})
})
})
// safe
return Promise.all(safePromises)
You would need to know how to identify an error in your results. If you do not have a standard expected error, I suggest that you run a transformation on each error in the catch block that makes it identifiable in your results.
try {
let resArray = await Promise.all(
state.routes.map(route => route.handler.promiseHandler().catch(e => e))
);
// in catch(e => e) you can transform your error to a type or object
// that makes it easier for you to identify whats an error in resArray
// e.g. if you expect your err objects to have e.type, you can filter
// all errors in the array eg
// let errResponse = resArray.filter(d => d && d.type === '<expected type>')
// let notNullResponse = resArray.filter(d => d)
} catch (err) {
// code related errors
}
Not the best way to error log, but you can always set everything to an array for the promiseAll, and store the resulting results into new variables.
If you use graphQL you need to postprocess the response regardless and if it doesn't find the correct reference it'll crash the app, narrowing down where the problem is at
const results = await Promise.all([
this.props.client.query({
query: GET_SPECIAL_DATES,
}),
this.props.client.query({
query: GET_SPECIAL_DATE_TYPES,
}),
this.props.client.query({
query: GET_ORDER_DATES,
}),
]).catch(e=>console.log(e,"error"));
const specialDates = results[0].data.specialDates;
const specialDateTypes = results[1].data.specialDateTypes;
const orderDates = results[2].data.orders;
Unfortunately, I don't have enough reputation to comment (or do much of anything, really), so I'm posting this as an answer in response to Eric's answer here.
The executor function can also be an async function. However, this is usually a mistake, for a few reasons:
If an async executor function throws an error, the error will be lost and won’t cause the newly-constructed Promise to reject. This could make it difficult to debug and handle some errors.
If a Promise executor function is using await, this is usually a sign that it is not actually necessary to use the new Promise constructor, or the scope of the new Promise constructor can be reduced.
From this explanation as to why Promises should not utilize an async executor function
Instead, you should opt for Promise.allSettled(), as suggested here by Asaf.
With the help of allSettled,we can now read the status of
each promise is, and process each error individually, without losing any of this critical information
const promises = [
fetch('/api/first'), // first
fetch('/api/second') // second
];
The simplest way is to handle errors
const [firstResult, secondResult] = await Promise.allSettled(promises)
// Process first
if (firstResult.status === 'rejected') {
const err = firstResult.reason
// Here you can handle error
} else {
const first = firstResult.value
}
// Process second
if (secondResult.status === 'rejected') {
const err = secondResult.reason
// Here you can handle error
} else {
const second = secondResult.value
}
A nice way to handle error
const results = await Promise.allSettled(promises);
const [first, second] = handleResults(results)
function handleResults(results) {
const errors = results.filter(result => result.status === 'rejected').map(result => result.reason)
if (errors.length) {
// Aggregate all errors into one
throw new AggregateError(errors)
}
return results.map(result => result.value)
}
That's how Promise.all is designed to work. If a single promise reject()'s, the entire method immediately fails.
There are use cases where one might want to have the Promise.all allowing for promises to fail. To make this happen, simply don't use any reject() statements in your promise. However, to ensure your app/script does not freeze in case any single underlying promise never gets a response, you need to put a timeout on it.
function getThing(uid,branch){
return new Promise(function (resolve, reject) {
xhr.get().then(function(res) {
if (res) {
resolve(res);
}
else {
resolve(null);
}
setTimeout(function(){reject('timeout')},10000)
}).catch(function(error) {
resolve(null);
});
});
}
I wrote a npm library to deal with this problem more beautiful.
https://github.com/wenshin/promiseallend
Install
npm i --save promiseallend
2017-02-25 new api, it's not break promise principles
const promiseAllEnd = require('promiseallend');
const promises = [Promise.resolve(1), Promise.reject('error'), Promise.resolve(2)];
const promisesObj = {k1: Promise.resolve(1), k2: Promise.reject('error'), k3: Promise.resolve(2)};
// input promises with array
promiseAllEnd(promises, {
unhandledRejection(error, index) {
// error is the original error which is 'error'.
// index is the index of array, it's a number.
console.log(error, index);
}
})
// will call, data is `[1, undefined, 2]`
.then(data => console.log(data))
// won't call
.catch(error => console.log(error.detail))
// input promises with object
promiseAllEnd(promisesObj, {
unhandledRejection(error, prop) {
// error is the original error.
// key is the property of object.
console.log(error, prop);
}
})
// will call, data is `{k1: 1, k3: 2}`
.then(data => console.log(data))
// won't call
.catch(error => console.log(error.detail))
// the same to `Promise.all`
promiseAllEnd(promises, {requireConfig: true})
// will call, `error.detail` is 'error', `error.key` is number 1.
.catch(error => console.log(error.detail))
// requireConfig is Array
promiseAllEnd(promises, {requireConfig: [false, true, false]})
// won't call
.then(data => console.log(data))
// will call, `error.detail` is 'error', `error.key` is number 1.
.catch(error => console.log(error.detail))
// requireConfig is Array
promiseAllEnd(promises, {requireConfig: [true, false, false]})
// will call, data is `[1, undefined, 2]`.
.then(data => console.log(data))
// won't call
.catch(error => console.log(error.detail))
————————————————————————————————
Old bad api, do not use it!
let promiseAllEnd = require('promiseallend');
// input promises with array
promiseAllEnd([Promise.resolve(1), Promise.reject('error'), Promise.resolve(2)])
.then(data => console.log(data)) // [1, undefined, 2]
.catch(error => console.log(error.errorsByKey)) // {1: 'error'}
// input promises with object
promiseAllEnd({k1: Promise.resolve(1), k2: Promise.reject('error'), k3: Promise.resolve(2)})
.then(data => console.log(data)) // {k1: 1, k3: 2}
.catch(error => console.log(error.errorsByKey)) // {k2: 'error'}

Intentionally not returning Bluebird Promise

I have the piece of code below. Want to call the callback which may return a promise. Resolve it. In case if the promise failed, log it. The caller should NOT know about all this and should return without waiting for the promise to fulfill. That's why I'm not returning the promise. This causes the following error:
(node:21146) Warning: a promise was created in a handler at internal/timers.js:456:21 but was not returned from it, see http goo.gl/rRqMUw
at Function.Promise.cast (bluebird/js/release/promise.js:225:13)
I've read the docs and they recommend returning null to prevent the warning from happening. Nevertheless, the warning still pops out. Also, do not want to disable the warning globally.
private _trigger(cb : () => Resolvable<any>)
{
try
{
const res = cb();
const prom = Promise.resolve(res);
prom.catch(reason => {
this.logger.error("ERROR: ", reason);
})
}
catch(reason)
{
this.logger.error("ERROR: ", reason);
}
return null;
}
The internal Promise should resolve to a value of null to silence the warning - this will tell Bluebird "The Promise isn't being returned, but since it resolves to null, it doesn't contain a useful result, so this is deliberate"; returning it wouldn't give the caller useful data. You can do something like:
const prom = Promise.resolve(res)
.catch(reason => {
this.logger.error("ERROR: ", reason);
})
.then(() => null);

Using Promise all to fill dropdowns

I'm using Promises to automatically fill up my dropdowns on page load (I have multiple dropdowns on the page).
Here is the code I use to return the following:
$(document).ready(function(){
var urls = ['getBrands', 'getTags'];
Promise.all(urls.map(u=>fetch(u))).then(
responses => Promise.all(responses.map(res => res.json()))
).then(
texts=>console.log(texts)
).then(
result => console.log(result[0]) //This is where the error is
)
});
This prints the response to the console correctly, but throws an error when I try to read the individual result. The error is Uncaught(in promise) TypeError: cannot read property '0' of undefined
The problem is your first fulfillment handler returns undefined, which becomes the fulfillment value of the promise it returns.
If you just remove it, your second fulfillment handler will see the values.
$(document).ready(function(){
var urls = ['getBrands', 'getTags'];
Promise.all(urls.map(u=>fetch(u))).then(
responses => Promise.all(responses.map(res => res.json()))
).then(
result => console.log(result[0])
)
});
Alternatively, have it return what it receives:
$(document).ready(function(){
var urls = ['getBrands', 'getTags'];
Promise.all(urls.map(u=>fetch(u))).then(
responses => Promise.all(responses.map(res => res.json()))
).then(texts => {
console.log(texts);
return texts;
}).then(
result => console.log(result[0])
)
});
Side note: That code breaks one of the Rules of Promises, which is:
Handle rejection, or pass the promise chain to something that will.
You probably want to add a rejection handler via .catch.
Side note 2: Assuming fetch is the standard fetch, your code is missing a check for success. This is a footgun in the fetch API (I write about it here). Fixing it:
Promise.all(
urls.map(u=>fetch(u).then(response => {
if (!response.ok) {
throw new Error("HTTP error " + response.status);
}
return res.json();
}))
).then(texts => {
Note that that eliminates the need for the second Promise.all as well, but handling each fetch individually earlier.

A returned Promise with Fetch doesn't respond to .then()

I'm a bit confused here, this part of some async code isn't working. Here's the problem:
export async function active() {
return new Promise(function(resolve,reject){
fetch("https://api.spotify.com/v1/me", { //omitted headers to keep it clean
}).then(response => console.log(response)) <-- This code runs, and will log a response
.then(data => function(){ <-- but this won't
console.log("This won't run either")
if (data.status == 200) {
console.log(true)
resolve(data)
} else {
console.log(false)
reject(data)
}
})
})
}
Why isn't the second part running? Sorry, I'm a bit new to async/await
There are a few problems there
.then(data => function(){ passes a function that returns a function into .then. You want either an arrow function or the function keyword but not both. Since all you're doing is returning a function (which nothing calls), you don't see the contents of that function run. Get rid of the function() part of that.
There's no reason for new Promise in that code (and no reason for your function to be async), just use the promise chain you have from fetch. (More here.)
Your first then handler returns undefined, because you're returning the result of console.log.
Your code uses the name data where you're still dealing with a Response object whose body you haven't yet read. You probably want to read the body, via the .text(), .json(), or other similar body-reading methods.
Instead:
export function active() {
return fetch("https://api.spotify.com/v1/me", { //omitted headers to keep it clean
})
.then(response => {
if (!repsonse.ok) {
throw new Error("HTTP status " + response.status);
}
return response.text(); // or `.json()` or several others, see #4 above
});
}
Note I changed the check for success there slightly.

Node js Aggregate response of 10 async calls

We have a requirement where we want to build api calls based on query param array for (contentId[]=1&contentId[]=2....and so on ), and then make async calls appending id to the api end point e.g http://xxxx/content/contentId.
Based on the response we need to aggregate and wrap the content with fields contentId, responsecode that we receive when we hit the individual api endpoints
{
{ "contentd": "1",
"responsecode": 200,
"messsage": "",
content{
}
}
{ "contentd": "2",
"responsecode": 200,
"messsage": "",
content{
}
...
}
We are using promise to do the same. I used promise all as below.
Promise.all(req.query.contentId
.map(function (contentId) {
console.log('processing the content id'+contentId);
return getContent(contentId);
}))
.then(function (all_content) {
//convert each resolved promised into JSON and convert it into an array.
res.json(all_content.map(function (content) {
return JSON.parse(content)
}));
})
.catch(function(rej) {
//here when you reject the promise
console.log("404 received"+rej);
}) ;
} catch (e) {
res.send(e.message);
}
});
// this is the function that makes the call to the backend. Note usage of request promise
function getContent(contentId) {
console.log('content id received to fetch the content'+contentId);
var options = {
uri: 'http://xxxxx/api/content/'+contentId,
headers: {
'Authorization': 'Basic YWRtaW46YWRtaW4='
},
qs: {
_format: 'json'
}
};
return rp(options);
}
Problems -The problem we are facing is that for the calls when we get http status code as 200 the result comes fine. However, for http status code 404(not found) the processing is not done. It seems to fail fast.
Also, how do I insert own fields for response, status while processing the content response as above in JSON.parse. thanks
If you read about Promise.all(), that's what it does. If any promise you pass it rejects, it immediately rejects the overall Promise.all() promise and you don't get the other results.
If you want all the other results, you have to either catch any rejections earlier so that Promise.all() never sees them or use a different way of tracking all the promises that is designed to get all respones, whether any reject or not.
You can catch the rejection earlier to "hide" it form Promise.all() by doing something like this and you also have to skip the JSON.parse() of any error objects that are passed through.
Promise.all(req.query.contentId
.map(function (contentId) {
console.log('processing the content id'+contentId);
// catch errors here and make the resolved value be the error object
// so Promise.all() processing continues on the other requests
return getContent(contentId).catch(e => e);
}))
.then(function (all_content) {
//convert each resolved promised into JSON and convert it into an array.
res.json(all_content.map(function (content) {
// make sure we don't try to parse things that are already objects
// like error objects
if (typeof content !== "string") {
return content;
} else {
return JSON.parse(content);
}
}));
}).then(function(results) {
// remove any error objects
// and probably log them to so they aren't just silently eaten
return results.filter(item => !(item instanceof Error));
})
Then, later down stream while using this array of results, you need to skip any error objects in the results.
Alternatively, you could make the results into some sort of no-op result that wouldn't affect your downstream processing or you can build a different array that had all error results filtered out of it.
And, if you want some prewritten code that just gets all the responses (including error results), that is often called Promise.settle(). There are several implementations of a Promise.settle() that you can use here:
ES6 Promise.all() error handle - Is .settle() needed?
Also, how do I insert own fields for response, status while processing the content response as above in JSON.parse.
You can change this:
return JSON.parse(content);
to something like this:
let obj = JSON.parse(content);
obj.someOtherProp = 'myvalue';
return obj;
return getContent(contentId);
Here you return the promise to Promise.all. so if the promise fails, it will trigger the Promise.all catch and all routes fail. So we need to catch earlier, so right there:
return getContent(contentId).catch( e => e);
That means in case of an error, proceed with the error as result. As the JSON.parse would fail then, you could return a json string instead:
return getContent(contentId).catch( e => '{"error":500}');
For the second question:
You may want to assign additional properties to the returned result:
return Object.assign( JSON.parse(content), {
what: "ever"
});

Categories

Resources