Node.JS recursive promise not resolving - javascript

i'm working with an API that allows me to sync data to a local DB. There is a syncReady API that I'm calling recursively until the sync batch is ready to start sending data. The recursion is working correctly and the .then callback is called, but the resolve function never resolves the response.
const request = require('request-promise');
const config = require('../Configs/config.json');
function Sync(){}
Sync.prototype.syncReady = function (token, batchID) {
return new Promise((res, rej) => {
config.headers.Get.authorization = `bearer ${token}`;
config.properties.SyncPrep.id = batchID;
request({url: config.url.SyncReady, method: config.Method.Get, headers: config.headers.Get, qs: config.properties.SyncPrep})
.then((response) => {
console.log(`The Response: ${response}`);
res(response);
}, (error) => {
console.log(error.statusCode);
if(error.statusCode === 497){
this.syncReady(token, batchID);
} else rej(error);
}
);
});
};
I get the 497 logged and the "The Response: {"pagesTotal";0}" response but the res(response) never sends the response down the chain. I've added a console.log message along the entire chain and none of the .then functions back down the chain are firing.
I hope I've explained this well enough :-). Any ideas why the promise isn't resolving?
Thanks!

First, you don't need to wrap something that returns a promise with a new Promise. Second, for your error case you don't resolve the promise if it is 497.
const request = require('request-promise');
const config = require('../Configs/config.json');
function Sync(){}
Sync.prototype.syncReady = function (token, batchID) {
config.headers.Get.authorization = `bearer ${token}`;
config.properties.SyncPrep.id = batchID;
return request({url: config.url.SyncReady, method: config.Method.Get, headers: config.headers.Get, qs: config.properties.SyncPrep})
.then((response) => {
console.log(`The Response: ${response}`);
return response;
})
.catch((error) => {
console.log(error.statusCode);
if(error.statusCode === 497){
return this.syncReady(token, batchID);
} else {
throw error;
}
})
);
};
Maybe something like the above will work for you instead. Maybe try the above instead. As a general rule of thumb, it's you almost always want to return a Promise.

Related

chaining responses with axios and Vue js

I am trying to chain three requests in one with Axios. I am practicing vue.js so please correct me if my approach is not ideal.
The goal is to have three requests. One is a post followed by 2 Gets. What I would like to achieve is one chained request.
My questions are:
Is this a good way to handle this? Should they be chained together like this?
Is it possible to map the response to a model like I did in the first post request and pass it to the next?
const apiClient: AxiosInstance = axios.create({
headers: {
'content-type': 'application/json',
'X-RapidAPI-Key': '08f852e........22fb3e2dc0...',
'X-RapidAPI-Host': 'judge0-ce.p.rapidapi.com'
},
params: {base64_encoded: 'true', fields: '*'},
});
export const api = {
async submitCode(code: Code) {
code.language_id = 60
code.stdin = "Sn..2Uw"
code.source_code = btoa(code.source_code)
apiClient.post<Token>(`https://judge0-ce.p.rapidapi.com/submissions?language_id=${code.language_id}&source_code=${code.source_code}&stdin=SnVkZ2Uw`)
.then(function (response) {
console.log("res.data", response.data.token);
}).then(function (token) {
console.log("token", token); // <---- empty
`https://judge0-ce.p.rapidapi.com/submissions/${token}` // <---- get request
}).then(function (response) {
console.log("res.data", response);
}).then(function (response ) {
// here I will need a model
})
.catch((err) => {
const error = err.response ? err.response.data : err;
console.log("error" + error);
})
}
}
You have to await each function if the next one is dependent on the previous. Or you could use Promise chaining in the traditional sense using new Promise(resolve, reject). Async only applies to top level, so you will need to declare subsequent functions 'async' again as shown.
I would also suggest setting axios defaults with a base URL so you don't have to repeat the full URL each time. Note that console.log statements are not "Thenable" so your 1st statement has no effect, nor does your 3rd, other than to define your next variable.
const apiClient = axios.create({
baseURL: 'https://judge0-ce.p.rapidapi.com/submissons',
// ... headers ... //
});
export const api = {
async submitCode(code){
// ... code variable ...//
await apiClient.post(
`?language_id=${code.language_id}&source_code=${code.source_code}&stdin=SnVkZ2Uw`)
.then(async (response1) => {
const token = response1.data.token
await api.get(`/${token}`)
}).then(async (response2) => {
console.log(response2.data)
const model = response2.data.map((val1) => apiClient.get(`anotherGet/${val1.id}`))
const result = await Promise.all(model)
return result.map((val2) => val2.data)
})
// ... catch ...//
}}
You useasync, then don't chain promices
async function submitCode(code: Code) {
code.language_id = 60
code.stdin = "Sn..2Uw"
code.source_code = btoa(code.source_code)
try { // ~ global `.catch`
const token = await apiClient.post<Token>(`https://blah`)
const get1result = await apiClient.get<Blah>(`https://balah?${token}`)
const get2result = await apiClient.get<Blah>(`https://balah?${token}`)
doBlah({token, get1result, get2result})
} catch (err) { // maybe should check object type here
const error = err.response ? err.response.data : err;
console.log("error" + error);
}
}
As for Vue, I can only recomment to use asyncComputed which you can feet Promise into if you need that
Express also had express.api or something with which you can skip https://blah/com/api/ part of url, check it

Promise.all() for a promiseArray not returning any values

I have the following setup where I am trying to make a call to a function which returns a Promise, which in turn has a Promise.all() call to make an indefinite number of axios.post() calls. I tried using axios.all() and axios.spread(...responses), but no luck. I've also tried a number of examples posted to SO from experienced users. But still no luck.
It's worth noting that the calls are being made, it's just that the responses being returned are undefined.
The function being called is the following:
createSurveyResult(surveyResults) {
let token = Vue.prototype.$auth.getLocalStorage(`${this.activeUser.uuid)_{process.env.NODE_ENV}_ACCESS_TOKEN`)
if (token) {
axiosModified.defaults.headers.common['Authorization'] = `Bearer ${token}`
}
return new Promise(resolve => {
let promiseArray = surveyResults.map(payload => {
axiosModified.post('/feedback/results/', payload)
})
Promise.all(promiseArray).then(responses => {
responses.forEach(res => console.log(res))
console.log('submitted all axios calls');
resolve(responses)
}).catch(error => {
resolve(error.response)
})
})
}
Which is being called as:
this.$surveys.createSurveyResult(surveyResults).then((responses) => {
console.log(responses)
});
Yet, both the console.log(res) res objects return are undefined within the Promise.all() , the console.log('submitted all axios calls'); is being called, and the responses from the then() callback are returned as:
I'm wondering what I am doing wrong here?
Write your function like this:
createSurveyResult(surveyResults) {
let token = Vue.prototype.$auth.getLocalStorage(
`${this.activeUser.uuid}_${process.env.NODE_ENV}_${ACCESS_TOKEN}`
);
if (token) {
axiosModified.defaults.headers.common["Authorization"] = `Bearer ${token}`;
}
return Promise.all(
surveyResults.map((payload) =>
axiosModified.post("/feedback/results/", payload)
)
);
}

fetch api throws uncaught in promise error

I have a wrapper function for the fetch api to fetch different endpoints to my api but somehow it keeps complaining that there is unhandled rejection TypeError: Cannot read property 'catch' of undefined
const apiRequest = (url) => {
return fetch()
.then(async resp =>{
const json = await resp.json()
if(json.status == "success") return json
return Promise.reject('err')
})
.catch(err => {
return Promise.reject(err)
})
}
calling the function like:
apiRequest('/test')
.then(data => console.log(data))
.catch(err => console.log(err))
what am I doing wrong?
Note: When this answer was posted, the question didn't have the return. The OP added it afterward, claiming it was in their original code. But they also accepted the answer, so something below must have helped... :-)
apiRequest needs to return the promise chain. Right now, it doesn't, so calling it returns undefined.
const apiRequest = (url) => {
return fetch(url) // *** Note: Added `url` here
// ^−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
.then(async resp =>{
const json = await resp.json()
if(json.status == "success") return json
return Promise.reject('err')
})
.catch(err => {
return Promise.reject(err)
})
}
But, three things:
There's no point whatsoever to that catch handler; and
You need to check for response success before calling json; and
There's no reason for that then handler to be an async function.
Instead:
const apiRequest = (url) => {
return fetch(url) // *** Note: Added `url` here
.then(resp => {
if (!resp.ok) {
throw new Error("HTTP status " + resp.status);
}
return resp.json();
})
};
(I've also added some missing semicolons there, but if you prefer to rely on ASI, just leave them off.)
If the fetch promise is rejected, that rejection will be carried through to the promise from apiRequest for the caller to handle. If the fetch promise is fulfilled but resp.ok is false (because there was an HTTP level error like a 404 or 500), the promise from apiRequest will be rejected with the error thrown in the then handler. If those things work but the json call fails, the promise from apiRequest will be rejected with the error from json. Otherwise, the promise from apiRequest will be fulfilled with the parsed data.
It can also be a concise form arrow function if you prefer:
const apiRequest = (url) => fetch(url).then(resp => { // *** Note: Added `url` to `fetch` call
if (!resp.ok) {
throw new Error("HTTP status " + resp.status);
}
return resp.json();
});
Your original code used an async function in then. If you can ues async functions in your environment, you may prefer to make apiRequest an async function:
const apiRequest = async (url) => {
const resp = await fetch(url); // *** Note: Added `url` here
if (!resp.ok) {
throw new Error("HTTP status " + resp.status);
}
return resp.json();
};

fetch promises - return 500 internals server error

I am trying to handle 500 internal server errors inside fetch. If an internal error occurs, the server responds with a message. I want to extract that message.
const req = new Request(url, {
method: node.method,
mode: 'cors',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
});
fetch(req)
.then((response) => {
if (response.status === 500) {
// res.json extracts the body from the response as a promise, chain
// .then on it and throw an error to be caught in the lower catch.
response.json()
.then((json) => {
const { message, stackTrace } = json;
throw new ServerException(message, stackTrace); // note 1
})
.catch((error) => {
return Promise.reject(RawException(error)); // note 2
});
} else {
return response.json();
}
})
.then((json) => { // note 3
dispatch(stopLoading());
dispatch(recieveResponse(typeOfRequest, json));
})
.catch((e) => {
dispatch(stopLoading());
dispatch(responseError());
dispatch(showError(e.message));
});
};
My issue is that extracting the body of the response creates a new promise, and I am unable to reject the outer promise from the inner one.
Note 1 triggers the catch method of the inner promise. Inside catch, I have tried throwing another error but it doesn't seem to work. If I throw new RawException(error) on the second noted line, nothing happens and the then method on the third noted line triggers. If I return a rejected promise as I have in the code provided, then still triggers but json is undefined.
How do I do this?
The solution is not to nest promises, but to resolve/return the .then of the outer promise with the conclusion of the inner promise.
if (response.status === 500) {
response.json() // response.json returns a promise, we chose to do nothing with its
.then((json) => { // conclusion
const { message, stackTrace } = json;
throw new ServerException(message, stackTrace); // note 1
})
.catch((error) => {
return Promise.reject(RawException(error)); // note 2
});
} else {
return response.json();
}
Should become
if (response.status === 500) {
return response.json() // return the result of the inner promise, which is an error
.then((json) => {
const { message, stackTrace } = json;
throw new ServerException(message, stackTrace);
});
} else {
return response.json();
}
The else clause can be removed as well if that syntax is preferred. ESLint complains about the else being wasteful, but I perfer the way it makes the code's branching explicit.

Reading the body returned for fetch() response which contains an HTTP error code [duplicate]

I have an HTTP API that returns JSON data both on success and on failure.
An example failure would look like this:
~ ◆ http get http://localhost:5000/api/isbn/2266202022
HTTP/1.1 400 BAD REQUEST
Content-Length: 171
Content-Type: application/json
Server: TornadoServer/4.0
{
"message": "There was an issue with at least some of the supplied values.",
"payload": {
"isbn": "Could not find match for ISBN."
},
"type": "validation"
}
What I want to achieve in my JavaScript code is something like this:
fetch(url)
.then((resp) => {
if (resp.status >= 200 && resp.status < 300) {
return resp.json();
} else {
// This does not work, since the Promise returned by `json()` is never fulfilled
return Promise.reject(resp.json());
}
})
.catch((error) => {
// Do something with the error object
}
// This does not work, since the Promise returned by `json()` is never fulfilled
return Promise.reject(resp.json());
Well, the resp.json promise will be fulfilled, only Promise.reject doesn't wait for it and immediately rejects with a promise.
I'll assume that you rather want to do the following:
fetch(url).then((resp) => {
let json = resp.json(); // there's always a body
if (resp.status >= 200 && resp.status < 300) {
return json;
} else {
return json.then(Promise.reject.bind(Promise));
}
})
(or, written explicitly)
return json.then(err => {throw err;});
Here's a somewhat cleaner approach that relies on response.ok and makes use of the underlying JSON data instead of the Promise returned by .json().
function myFetchWrapper(url) {
return fetch(url).then(response => {
return response.json().then(json => {
return response.ok ? json : Promise.reject(json);
});
});
}
// This should trigger the .then() with the JSON response,
// since the response is an HTTP 200.
myFetchWrapper('http://api.openweathermap.org/data/2.5/weather?q=Brooklyn,NY').then(console.log.bind(console));
// This should trigger the .catch() with the JSON response,
// since the response is an HTTP 400.
myFetchWrapper('https://content.googleapis.com/youtube/v3/search').catch(console.warn.bind(console));
The solution above from Jeff Posnick is my favourite way of doing it, but the nesting is pretty ugly.
With the newer async/await syntax we can do it in a more synchronous looking way, without the ugly nesting that can quickly become confusing.
async function myFetchWrapper(url) {
const response = await fetch(url);
const json = await response.json();
return response.ok ? json : Promise.reject(json);
}
This works because, an async function always returns a promise and once we have the JSON we can then decide how to return it based on the response status (using response.ok).
You would error handle the same way as you would in Jeff's answer, however you could also use try/catch, an error handling higher order function, or with some modification to prevent the promise rejecting you can use my favourite technique that ensures error handling is enforced as part of the developer experience.
const url = 'http://api.openweathermap.org/data/2.5/weather?q=Brooklyn,NY'
// Example with Promises
myFetchWrapper(url)
.then((res) => ...)
.catch((err) => ...);
// Example with try/catch (presuming wrapped in an async function)
try {
const data = await myFetchWrapper(url);
...
} catch (err) {
throw new Error(err.message);
}
Also worth reading MDN - Checking that the fetch was successful for why we have to do this, essentially a fetch request only rejects with network errors, getting a 404 is not a network error.
I found my solution at MDN:
function fetchAndDecode(url) {
return fetch(url).then(response => {
if(!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
} else {
return response.blob();
}
})
}
let coffee = fetchAndDecode('coffee.jpg');
let tea = fetchAndDecode('tea.jpg');
Promise.any([coffee, tea]).then(value => {
let objectURL = URL.createObjectURL(value);
let image = document.createElement('img');
image.src = objectURL;
document.body.appendChild(image);
})
.catch(e => {
console.log(e.message);
});
Maybe this option can be valid
new Promise((resolve, reject) => {
fetch(url)
.then(async (response) => {
const data = await response.json();
return { statusCode: response.status, body: data };
})
.then((response) => {
if (response.statusCode >= 200 && response.statusCode < 300) {
resolve(response.body);
} else {
reject(response.body);
}
})
});

Categories