NodeJS seems to execute 2 calls to the same function in parallel - javascript

I have an axios interceptor
let authTokenRequest = null
axios.interceptors.response.use(undefined, err => {
const error = err.response
if (error.status === 401 && error.config && !error.config.__isRetryRequest) {
return getAuthToken()
.then(response => {
saveTokens(response.data)
error.config.__isRetryRequest = true
return axios(error.config)
})
.catch(e => {
throw e
})
.finally(() => {
authTokenRequest = null
})
}
})
This calls the following function
function getAuthToken() {
if (!authTokenRequest) {
authTokenRequest = RefreshToken();
}
return authTokenRequest;
}
So I'd expect the RefreshToken function to only be called once.
But it's called twice in quick succession, even tho authTokenRequest is quickly assigned the promise inside the if statement making it truthy - so a second call should not be possible?
This behaves correctly in the browser but not in NodeJS, any reason why?

Related

Promise is resolved to undefined

I am sorry if it is simple question, I'm new to javascript
So I have simple axios GET request. It is used three times in my code, so I thought that I could make it an external function, to avoid duplicating code, to making it cleaner and easy readable
The problem is when I call to that function, return value is undefined. And this is because code is working like synchronous. So I thought that I need to make the function return a Promise, and in function call I have to use async/await or then syntax to get the response in the right time. But after many tries code is still running as synchronous
I read a lot of theory on promises and how they work, got a solid understanding of when they change states, but something goes wrong when I try to implement them on my own
Here is the function that retrieves data
const getMessagesFromChat = async (JWT_header, chatId) => {
if (JWT_header !== '') {
//1 let messages
//4 return
axios.get(`${SECURED_API_PATH}/messages/chat/${chatId}`, {
headers: {authorization: JWT_header},
params: {size: 80, page: 0}
})
.then(response => {
console.log('messages (fetch)', response.data)
//1 messages = response.data
//1 return messages
return response.data //2
//3 return Promise.resolve(response.data)
})
.catch(error => {
console.log(error, error.response)
})
//5 return Promise.resolve(messages)
}
}
I marked comments with numbers based on what I've tried
make a variable and return it in then block
make function async so everything it returns is wrapped in promise
return explicit promise using Promise.resolve() in then block
return the whole request as a promise
return explicit promise using Promise.resolve() after the request
All responses except 4 were undefined. In 4 variant log statement shows the promise object itself instead of promise value. In other variants log statement shows first `undefined` response and then the actual response from request
I tried to implement second advice from
https://stackoverflow.com/questions/45620694/how-to-return-response-of-axios-in-return/45622080
but it did not work.
This is the function I tried to use to get the result (onClick)##
const selectChat = () => {
const JWT_header = getToken()
if (JWT_header !== null) {
try {
const messages = await getMessagesFromChat(JWT_header, chatId)
console.log('messages (after fetch)', messages)
//setMessages(messages)
} catch (error) {
console.log(error)
}
} else {
setIsLoggedIn(false)
}
or
const selectChat = () => {
const JWT_header = getToken()
if (JWT_header !== null) {
getMessagesFromChat(JWT_header, chatId)
.then(response => {
console.log(response)
setMessages(response)
})
.catch (error =>console.log(error))
} else {
setIsLoggedIn(false)
}
But none of them worked as expected
What am I doing wrong?
Your function doesn't return anything. A return statement in a then callback is not sufficient, you'd still have to return the promise chain itself from the outer function.
Use either .then() syntax:
function getMessagesFromChat(JWT_header, chatId) { // no `async` here
if (JWT_header !== '') {
return axios.get(`${SECURED_API_PATH}/messages/chat/${chatId}`, {
// ^^^^^^
headers: {authorization: JWT_header},
params: {size: 80, page: 0}
}).then(response => {
console.log('messages (fetch)', response.data)
return response.data
})
} else {
return Promise.resolve(undefined)
// ^^^^^^ important for chaining
}
}
or async/await syntax:
async function getMessagesFromChat(JWT_header, chatId) {
if (JWT_header !== '') {
const respnse = await axios.get(`${SECURED_API_PATH}/messages/chat/${chatId}`, {
// ^^^^^
headers: {authorization: JWT_header},
params: {size: 80, page: 0}
});
console.log('messages (fetch)', response.data)
return response.data
}
// else return undefined
}

javascript promise handling, fail to handle error

I'm having some trouble understanding what I'm doing wrong. I have a function that receives a url to which should make a GET request, in case of success should fill a combo with the received data (this depends which function calls it), in case of fail it should execute some common code.
getFirstCombo = () => {
this.getFromApi('/First/GetAll')
.then(data => this.setState({firstComboOptions: this.parseCombo(data)}))
.catch(error => console.log('ERROR2: ', error));
}
getSecondCombo = () => {
this.getFromApi('/Second/GetAll')
.then(data => this.setState({secondComboOptions: this.parseCombo(data)}))
.catch(error => console.log('ERROR2: ', error));
}
parseCombo = (data: any) => {
const combo = data.map(item => (
{ label: item.description, value: item.id }
));
return combo;
}
getFromApi = (url: string) : Promise<any> => {
return restApiAxios.get(url)
.then(response => {
return response.data;
})
.catch(error => {
console.log('ERROR: ', error);
});
}
this code is executed on the componentDidMount of the react component, but when it fails, it first prints :
ERROR: Error: Network Error
at createError (createError.js:16)
at XMLHttpRequest.handleError (xhr.js:83)
and immediately after:
PanelDatos.tsx:50 ERROR2: TypeError: Cannot read property 'map' of undefined
at PanelDatos.parseCombo (PanelDatos.tsx:55)
at PanelDatos.tsx:50
so, when failing executes the catch block from getFromApi and then it tries to execute the then block in getFirstCombo, which triggers the catch block from the same function cause data does not exist, why is that? shouldnt it just execute the first catch?
thanks in advance
.catch returns a promise much like .then, allowing you to return a custom value and handle it that way.
Try doing the following to observe the effect:
Promise
.reject(1)
.catch(e => e) // Catch the error and return it
.then(console.log) // will log 1 to the console
This means you'll need to add some checks if you want to continue to use promises like this:
Promise
.reject(new Error('haha'))
.catch(err => ({err}))
.then(({err, data}) => {
if(err) return // Do nothing
// enter code here
})
However, using async / await will improve readability even more:
getFirstCombo = async () => {
let response
try {
response = await this.getFromApi('/First/GetAll')
} catch (e) {
return // Exit early
}
let parsed
try {
parsed = this.parseCombo(data)
} catch (e) {
console.log(e)
return // Exit early
}
return this.setState({firstComboOptions: parsed})
}
And, of course, throw the error again in your catch block in your api to allow it to handle api calls.
This is happening since inside getFromApi catch method on the error you are not returning anything, so by default, it is returning a resolved promise with null response and the execution goes inside getFirstCombo then method, causing another error. You can update your code to resolve this like:
getFromApi = (url: string): Promise<any> => {
return restApiAxios.get(url)
.then(response => response.data)
.catch(error => Promise.reject(error));
}
The static Promise.reject function returns a Promise that is rejected. So, it will go directly into catch of wherever getFromApi is called.
DEMO:
async function getFromApi(url) {
return fetch(url) // rejects
.then(response => response.json())
.catch(err => Promise.reject(err))
}
async function getFirstCombo() {
getFromApi('https://no-such-server.abcd')
.then(data => console.log('data: ', data))
.catch(error => console.log('ERROR2: ', error));
}
getFirstCombo()
DEMO #2 (With getFirstCombo function not having any catch block) :
async function getFromApi(url) {
return fetch(url) // rejects
.then(response => response.json())
.catch(err => {
console.log('ERROR in getFromApi(): ', err);
return null; // return null, empty array, 0 or false... as per your requirement
})
}
async function getFirstCombo() {
getFromApi('https://no-such-server.abcd')
.then(data => console.log('data: ', data))
// Same value set in catch block of getFromApi will return in this then() block
// Validate this `data` variable before processing it further like:
// if(data === null) this means an error had occurred
// else continue with your logic
}
getFirstCombo()

Vuex action not waiting to finish axios promise

I encounter a strange situation developing an application in Laravel + VueJS/Vuex stack.
I understand that if a promise is not returned the parent function calling it will not wait for it to resolve so things will go asynchronous. Axios returns a promise by default when calling a resourse through http.
So i have the parent function which looks like this:
fetchInvoiceSeries() {
var arr = []
let invsrs = this.$store.getters['getInvoiceSeries']
if (invsrs == null) {
return this.$store
.dispatch('get_invoice_series')
.then(() => {
invsrs = this.$store.getters['getInvoiceSeries']
if (invsrs != null) {
invsrs.forEach(function(s) {
arr.push({
value: s.id,
text: s.series + ' / ' + s.increment
})
})
this.series = arr
} else {
console.log('Error while fetching invoice series!')
}
})
.catch(e => {
console.log(e)
})
} else {
invsrs.forEach(function(s) {
arr.push({
value: s.id,
text: s.series + ' / ' + s.increment
})
})
this.series = arr
}
}
And here is the function defined in action part of the vuex module:
get_invoice_series({ commit }) {
return get('/api/series/0')
.then(response => {
if (response.data && typeof response.data !== undefined) {
let payload = response.data
commit('SET_INVOICE_SERIES', payload)
} else {
console.log('error', error)
}
})
.catch(error => {
console.log('error', error)
})
},
So as you can see i am returning the get request from axios inside the action. In the parent i am calling the action and the "then" keyword in order to do some processing after the action it's done. Also i am using arrow function because i need the context in the parent function in order to call this.$store ...
The problem is that even after checking the getter to see if the state have the invoice series and getting them using the get_invoice_series action i still don't have the invoice series in memory judging by the code i wrote. The console keeps loggin 'Error while fetching invoice series!' the first time i execute the code and the second time (after the information exists in state), the code skips fetching the invoice series (as expected).
Can you tell me what i am doing wrong ? Thank you!
Your error comes from invsrs being null the first time, and not null the second time.
This means that your function get_invoice_series({ commit }) is asynchronous, and that it returns a promise.
For more readability, maybe you should make your call independently from your return statement, using async/await expressions :
async get_invoice_series({ commit }) {
const response = await get('/api/series/0')
if (response.data === undefined) return null
const payload = response.data
commit('SET_INVOICE_SERIES', payload)
return payload
},
And then make your calls wait for this fetch to process :
async fetchInvoiceSeries() {
let arr = []
const invsrs = await this.$store.getters['getInvoiceSeries']
// ...
It's pure conjecture here, let me know if it helps or not.

Mock api call to fail a certain number of times with jest and axios

I have a function that sets up an api call and then passes this function to a retry function which executes the call and retries the call, up to 5 times, if it fails.
public someAPICall() {
const promiseFunction = () => axios.post('/some/path/', {
headers: {
//some headers in here
},
});
return executePromise(promiseFunction, 5);
}
private async executePromise(promiseFunction, numberOfTimesToTryAgain) {
try {
// execute api call
return await promiseFunction();
}
catch (error) {
if (retries > 0) {
// try api call again if it has failed less than 5 times
return executePromise(promiseFunction, --numberOfTimesToTryAgain);
}
return error;
}
}
I'd like to test someAPICall and mock the results of the axios call to fail a certain amount of times using jest.
I can mock the axios.post call in my test file doing this:
jest.mock('axios');
mockPost = jest.fn(() => {});
(<jest.Mock>axios.create) = jest.fn(() => {
return {
post: mockPost
};
});
But how can I make it so the post call fails, for example, 3 times and then succeeds?
Got it:
mockPostFailTwice = jest.fn()
.mockImplementation(() => {
return 200;
})
.mockImplementationOnce(() => {
throw 500;
})
.mockImplementationOnce(() => {
throw 500;
});
This causes the post execution to throw a 500 error the first 2 times and return a 200 on the third.

How to make sure that the Observable.map only executed on succes?

I just want to make sure that code inside map function should be called only on success but not on failure.
delete(department: Department): Observable<Department[]> {
return this.post('/delete', body).map(response => {
let index: number = this.departments.indexOf(department);
if (index > -1) {
this.departments.splice(index, 1);
}
return this.departments;
});
}
I don't know whether the code inside map executes only on success or not. And also, I have to return an Observable so , I can't apply subscribe function here.
Observable#map operator will be executed on success response only (e.g. status 200). Observable#catch operator is intended to catch failures.
Also, Observable#catch operator will catch javascript errors thrown while mapping success response as well. Example:
fetchDashboardData(): Observable<Dashboard> {
return this._http.get(reqUrl, reqOptions)
.map((response: Response) => new Dashboard(response.json().items[0].dashboard))
.catch((error: any) => {
if (error instanceof Error) {
// js error, e.g. response.json().items was an empty array
console.log(error); // => “Cannot read property 'dashboard' of undefined...
return Observable.throw('Incomplete response data!');
} else {
return Observable.throw('Server error!');
}
});
}
Subscribe block:
this.dashboardService.fetchDashboardData().subscribe(
(dashboard: Dashboard) => {
this.dashboard = dashboard;
console.log('Success fetching dashboard data!', dashboard);
},
(errMssg: string) => {
console.error(errMssg); // => 'Incomplete response data!'
// or 'Server error!'
},
() => {
// finally block!
}
);

Categories

Resources