promise chaining reject - javascript

I am coding this chained promises.
First, when a button is clicked, it checks if a file url exists:
If not, it rejects, and then the response status is shown in an alert.
If yes, then it updates the DB via a webapi, then update the react state.
The problem I faced is, even I rejected in validateResponse function, it still run the next then.
I think it should go to the catch directly.
Besides, the below code to call webapi seems not good, a promise inside a then, etc. also the whole code seems unclear? is it a better way to do so?
onClick: (event, row) => {
function validateResponse(response) {
if (!response.ok) { // assume it is the reject case.
console.log("file not ready");
return Promise.reject(response.statusText);
} else {
window.open(response.url, '_blank', 'location=yes,height=500,width=600,scrollbars=no,status=yes')
return response;
}
}
fetch(row.fileurl, {
method: 'HEAD'
})
.then(validateResponse)
.then(console.log("== this line not printed, due to rejected."))
.then(row.linked = 1)
.then(
fetch(this.server_url+'/file/linked', { method: 'POST', body: JSON.stringify(row), headers: { 'Content-Type': 'application/json' }, })
.then(res => {
console.log("== it should be rejected!, why printed this line2")
if (res.status==200) {
this.setState({ row });
} else {
row.checked = 0;
throw Error(res.status);
}
})
)
.catch(function (error) {
alert("Sorry, the file is not avaliable yet")
});
}
One more question:
.then(() => row.linked = 1)
.then(() => fetch(this.server_url+'/file/linked', { method: 'POST', body: JSON.stringify(row), headers: { 'Content-Type': 'application/json' }, })
how to combine this into one?
.then(() => row.linked = 1 && fetch(this.server_url+'/file/linked', { method: 'POST', body: JSON.stringify(row), headers: { 'Content-Type': 'application/json' }, })
is it a better/correct way to do so?

Returning the Promise.reject should make it work.
Issue is that when you don't specify a return value in a .then it will resolve the promise with an undefined value by default.
In your case, you should change your validateResponse so it returns the rejected promise:
return Promise.reject(response.statusText);
Check this for more info.
=================
Edit: Try with this code
onClick: (event, row) => {
function validateResponse(response) {
if (!response.ok) { // assume it is the reject case.
console.log("file not ready");
return Promise.reject(response.statusText);
} else {
window.open(response.url, '_blank', 'location=yes,height=500,width=600,scrollbars=no,status=yes')
return response;
}
}
fetch(row.fileurl, {
method: 'HEAD'
})
.then(validateResponse)
.then(() => console.log("== this line not printed, due to rejected."))
.then(() => row.linked = 1)
.then(() => fetch(this.server_url+'/file/linked', { method: 'POST', body: JSON.stringify(row), headers: { 'Content-Type': 'application/json' }, })
.then(res => {
console.log("== it should be rejected!, why printed this line2")
if (res.status==200) {
this.setState({ row });
} else {
row.checked = 0;
throw Error(res.status);
}
})
)
.catch(function (error) {
alert("Sorry, the file is not avaliable yet")
});
}
========================
Edit2: .then accepts a function as callback. This means you can put it in one big function if you want:
onClick: (event, row) => {
function validateResponse(response) {
if (!response.ok) { // assume it is the reject case.
console.log("file not ready");
return Promise.reject(response.statusText);
} else {
window.open(response.url, '_blank', 'location=yes,height=500,width=600,scrollbars=no,status=yes')
return response;
}
}
fetch(row.fileurl, {
method: 'HEAD'
})
.then(validateResponse)
.then(() => {
console.log("== this line not printed, due to rejected.");
row.linked = 1;
return fetch(this.server_url+'/file/linked', { method: 'POST', body: JSON.stringify(row), headers: { 'Content-Type': 'application/json' }, })
.then(res => {
console.log("== it should be rejected!, why printed this line2")
if (res.status==200) {
this.setState({ row });
} else {
row.checked = 0;
throw Error(res.status);
}
})
})
.catch(function (error) {
alert("Sorry, the file is not avaliable yet")
});
}

You aren't calling your second fetch in a callback and that is causing the fetch to fire immediately.
function someFunc() {
// You are invoking the second fetch immediately
fetch("example.com")
.then(validate)
.then(fetch("somewhere.com"))
// You need to invoke it as a callback
fetch("example.com")
.then(validate)
.then(() => fetch("somewhere.com"))
// Or with non-arrow
fetch("example.com")
.then(validate)
.then(function() {
return fetch("somewhere.com");
});
}

Related

Problem with a 'double fetch' call in React Native

I am having problems using 'nested' Fetch calls within a React Native function. It seems the first Fetch works correctly, however an error is thrown on the second. Here is the code:
//****CALL TWO FETCH REQUESTS...
const data = { passkey: '12345', callup: 'name' };
const secondary = { passkey: '12345', callup: 'name' };
fetch('https://myremoteserveraddress', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
})
.then(function(response) {
if (response.ok) {
return response.json();
} else {
return Promise.reject(response);
}
})
.then(data => {
// Store the post data to a variable
_post = data;
console.log('Success on FIRST FETCH:', data);
console.log('answer is:', data.answer);
console.log('answer is:', _post.answer);
// Fetch another API
fetch('https://myremoteserveraddress', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(secondary),
})
})
.then(function (response) {
if (response.ok) {
return response.json();
} else {
return Promise.reject(response);
}
})
.then(function (userData) {
console.log('Returned from BOTH fetch calls'); //does not write to console
console.log(_post, userData); //does not write to console
this.vb.start();
})
.catch((error) => {
console.error('Error in onPressPublishBtn:', error);
});
//****
It seems the second Fetch call returns 'undefined', despite being identical to the first Fetch call which seems to work successfully. The error returned is "TypeError: undefined is not an object (evaluating 'response.ok')". If anybody can advise on what the problem may be I would be greatly appreciative. Thank you in advance.
You should return a Promise from the second then(...) block so that the response is passed to the third then(...) block. You might want to try something like this:
// Fetch another API
return fetch('https://myremoteserveraddress', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(secondary),
})

How to return asynchronous calls in JavaScript with two callbacks (WIX corvid)

The following asynchronous function returns 2 callbacks, so I did what I usually do to return the response
Added return before the fetch
Added return before the result (json.access_token)
but this time console.log(httpResponse, 'fetch json') is undefined on the console and console.log(json.access_token) return the right value.
What do I need to change?
From client
GetJSON(NewURLCode).then(httpResponse => {
console.log(httpResponse, 'fetch json')
}
From Server
GetJSON(NewURLCode){
return fetch("https://accounts.google.com/o/oauth2/token", {
"method": "post",
"headers": {
"Content-Type": 'application/x-www-form-urlencoded'
},
'body': data
}).then((httpResponse) => {
if (httpResponse.ok) {
return httpResponse.json();
} else {
return Promise.reject("Fetch did not succeed");
}
}).then((json) => {
console.log(json.access_token)
return json.access_token
}).catch(err => console.log(err));
}
Unlike the previous function, the following function has only one promise and returns the right object from the client
client
insert_coll ('Token',toInsert).then((ins_result) => {consule.log(ins_result)}
Backend
insert_coll{
return wixData.update(myCollection, toUpdate, options)
.then( ( results) => {
let item = results; //see item below
return results
} )
.catch( (err) => {
let errorMsg = err;
} );
}
}
The error was not accrued because of the return statement, I added a return before httpResponse.json();
It caused because the WIX onReady component called twice...
So for solving this issue I used the following WIX solution :
$w.onReady(function () {
if (wixWindow.rendering.env === "browser") {
//your code
}})
You can return that fetch itself, and handle those stuffs in client.
Like,
Server
GetJSON(NewURLCode){
return fetch("https://accounts.google.com/o/oauth2/token", {
"method": "post",
"headers": {
"Content-Type": 'application/x-www-form-urlencoded'
},
'body': data
});
}
Client
GetJSON(NewURLCode).then(httpResponse => {
if (httpResponse.ok) {
httpResponse.json();
} else {
Promise.reject("Fetch did not succeed");
}
}).then((json) => {
console.log(json.access_token)
return json.access_token
}).catch(err => console.log(err));
}
Otherwise, just return a Promise after those are resolved in the server. Like,
Server
async GetJSON(NewURLCode){
return new Promise(async (resolve, reject) => {
try {
const httpResponse= await fetch("https://accounts.google.com/o/oauth2/token", {
"method": "post",
"headers": {
"Content-Type": 'application/x-www-form-urlencoded'
},
'body': data
});
if (httpResponse.ok) {
const json = await httpResponse.json();
resolve(res.access_token);
} else {
reject("Fetch did not succeed");
}
} catch(err) {
reject(err);
}
});
}
Client
GetJSON(NewURLCode).then(httpResponse => {
console.log(httpResponse, 'fetch json')
}).catch(err => console.log(err));

Fetch API error handling

I want to display error message from my API, problem is that I can't reach that error if I check for response.ok, it returns Fetch error, not the one from API..
If I don't use if(response.ok)... it returns the error from API but it dispatches the success action.
Here is the example, login action:
export const signIn = data => dispatch => {
dispatch({
type: SIGN_IN
})
fetch(API_URL+'/login', {
method: 'POST',
headers: {
'content-type': 'application/json'
},
body: JSON.stringify(data),
})
.then( response => {
if (!response.ok) { throw response }
return response.json() //we only get here if there is no error
})
.then( json => {
dispatch({
type: SIGN_IN_SUCCESS, payload: json
}),
localStorage.setItem("token", 'Bearer '+json.token)
localStorage.setItem("user", JSON.stringify(json.user))
})
.catch( err => {
dispatch({
type: SIGN_IN_FAILED, payload: err
})
})
}
This is the code for action that dispatches the right message but as success action, not as failed one..
export const signIn = data => dispatch => {
dispatch({
type: SIGN_IN
})
fetch(API_URL+'/login', {
method: 'POST',
headers: {
'content-type': 'application/json'
},
body: JSON.stringify(data),
})
.then( response => response.json())
.then( json => {
dispatch({
type: SIGN_IN_SUCCESS, payload: json
}),
localStorage.setItem("token", 'Bearer '+json.token)
localStorage.setItem("user", JSON.stringify(json.user))
})
.catch( err => {
dispatch({
type: SIGN_IN_FAILED, payload: err
})
})
}
With the following solution one can handle JSON API error, Generic API error and Generic fetch error
fetch("api/v1/demo", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
"data": "demo"
})
})
.then(response => {
if (!response.ok) {
return Promise.reject(response);
}
return response.json();
})
.then(data => {
console.log("Success");
console.log(data);
})
.catch(error => {
if (typeof error.json === "function") {
error.json().then(jsonError => {
console.log("Json error from API");
console.log(jsonError);
}).catch(genericError => {
console.log("Generic error from API");
console.log(error.statusText);
});
} else {
console.log("Fetch error");
console.log(error);
}
});
according to This Article :
Per MDN, the fetch() API only rejects a promise when
“a network
error is encountered, although this usually means permissions issues
or similar.”
Basically fetch() will only reject a promise if the user
is offline, or some unlikely networking error occurs, such a DNS
lookup failure.
then, you can use this part of code to use non-network error handlings and make your code more readable
function handleErrors(response) {
if (!response.ok) throw new Error(response.status);
return response;
}
fetch("API URL")
// handle network err/success
.then(handleErrors)
// use response of network on fetch Promise resolve
.then(response => console.log("ok") )
// handle fetch Promise error
.catch(error => console.log(error) );
In order to extract API message from server in case of some error, you have to use the following idiom (which doesn't lie on the surface though), see link
fetch("http://localhost:8090/test/error", {
method: 'GET',
headers: {
'Accept': 'application/json'
}
})
.then(result => {
//Here body is not ready yet, throw promise
if (!result.ok) throw result;
return result.json();
})
.then(result => {
//Successful request processing
console.log(result);
}).catch(error => {
//Here is still promise
console.log(error);
error.json().then((body) => {
//Here is already the payload from API
console.log(body);
});
})
Verbose - yes!, but does exactly what is needed.

Jest is green even if Expected is not equal Received

it('User is already present as a supplier', (done) => {
const store = mockStore({}, [{ type: 'get_user', data: { } }]);
return store.dispatch(userGetAction({ role: 'supplier' }, () => {})).then(() => {
try {
expect(store.getActions()[0].data.disabled).toEqual(true);
expect(store.getActions()[0].data.errormessage).toEqual('User is already present as a assitantbuyer');
} catch (err) {
console.log(err);
}
done();
}).catch(() => {
done();
});
});
Why it is passing and showing green status even it is Expected is not equal to Actual.
PASS src/actions/user-get-action-assistant-buyer.test.jsx
● Console
console.error node_modules/fbjs/lib/warning.js:33
console.log src/actions/user-get-action-assistant-buyer.test.jsx:25
{ Error: expect(received).toEqual(expected)
Expected value to equal:
"User is already present"
Received:
"User is already present"
at store.dispatch.then (/Users/prakashchandrabarnwal/Desktop/myProductInduct_CE_Admin/src/actions/user-get-action-assistant-buyer.test.jsx:23:57)
matcherResult:
{ actual: 'User is already present as a assitant buyer',
expected: 'User is already present as a assitantbuyer',
message: [Function],
name: 'toEqual',
pass: false } }
If i do not wrap expect inside try catch its silently going inside .catch()
thunk code returning UnhandledPromiseRejectionWarning:
const buyerAction = (data = {}, cb) => dispatch => axios({
method: 'POST',
url: `http://localhost:3001/api/manageUsers`,
headers: {
'x-access-token': authService.getAccessToken()
},
data
}).then(res => new Promise((resolve, reject) => {
if (res.status === 200 && res.data) {
dispatch({ type: 'buyer_created', data: res.data.message });
if (data.role === 'buyer') {
axios({
method: 'POST',
url: `http://localhost:3001/api/populateBuyerLookUp`,
headers: {
'x-access-token': authService.getAccessToken()
},
data
})
.then((response) => {
resolve(response);
}).catch((err) => {
reject(err);
});
}
cb(res.data.message);
} else {
reject(res);
}
}))
.catch(() => {
console.log('error');
});
(node:44182) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
So, your expect fails with an error, you catch this error and just log it, in other words, you mute the error. After all, you call "done" just like there is no error.
The code doing things just like you wrote: ignore and mute any errors. You have to remove all catch from your test
it('User is already present as a supplier', () => {
const store = mockStore({}, [{ type: 'get_user', data: { } }]);
return store
.dispatch(userGetAction({ role: 'supplier' }, () => {}))
.then(() => {
expect(store.getActions()[0].data.disabled).toEqual(true);
expect(store.getActions()[0].data.errormessage).toEqual('User is already present as a assitantbuyer');
});
});
You can return Promise from your test (There is no need to use done) and if promise fails, the whole test fails. that's all
UPD: regarding UnhandledPromiseRejectionWarning, I think it could be linked with your request to the "populateBuyerLookUp", this request completely out of the flow. I tried to fix it, but it difficult to understand what you want to do exactly
const buyerAction = (data = {}, cb) => dispatch => axios({
method: 'POST',
url: `http://localhost:3001/api/manageUsers`,
headers: {
'x-access-token': authService.getAccessToken()
},
data
})
.then((res) => {
dispatch({type: 'buyer_created', data: res.data.message});
let promise;
if (data.role === 'buyer') {
promise = axios({
method: 'POST',
url: `http://localhost:3001/api/populateBuyerLookUp`,
headers: {
'x-access-token': authService.getAccessToken()
},
data
});
}
return Promise.resolve(promise).then(() => res.data.message);
})
.then((message) => {
cb(message)
}).catch(()=>{console.log("error")});

Promise based Action succeeds, data gets resolved but i get an error message

So i'm trying to build this action on a react and i need it as a promise.
The action succeeds, i receive the response from the server but i also get an error saying:
VM89852:98 Uncaught TypeError: Cannot read property 'then' of undefined.
action:
export const fetchAllAccounts = (token, dispatch) => {
return new Promise((resolve, reject) => {
fetchAccountsStart((dispatch));
return axios.get(`${API_URL}/accounts`, {
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
}).then(
(response) => {
fetchAccountsSuccess(dispatch, response.data);
resolve(response.data);
},(error) => {
fetchAccountsFailed(dispatch, error.data);
reject(error.data);
},
);
});
};
Also heres the method on how i call this action.
this.props.fetchAllAccounts(token)
.then((data) => {
console.log("#".repeat(120));
console.log(data);
console.log("#".repeat(120));
}).catch((error) => {
console.log("#".repeat(120));
console.log(error);
console.log("#".repeat(120));
});
your comment
heres the call from mapDispatchToProps ...
fetchAllAccounts: (token) => { fetchAllAccounts(token, dispatch) },
There is your problem, in the comment. This either needs to be
fetchAllAccounts: (token) => { return fetchAllAccounts(token, dispatch) },
or
fetchAllAccounts: (token) => fetchAllAccounts(token, dispatch),
Understand that with arrow functions, if you use {} you need to return, there is no implied return
As a bonus - remove the promise constructor anti-pattern
export const fetchAllAccounts = (token, dispatch) => {
fetchAccountsStart((dispatch));
return axios.get(`${API_URL}/accounts`, {
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
}).then(
(response) => {
fetchAccountsSuccess(dispatch, response.data);
return response.data;
}, (error) => {
fetchAccountsFailed(dispatch, error.data);
throw error.data;
// Borrowed from #T.J.Crowder's pastebin :p
// Note that it's best to reject or throw an Error instance,
// not other types; e.g., `throw new Error(error.data)` or
// `return Promise.reject(new Error(error.data))`
},
);
};

Categories

Resources