I am wanting to refactor a Promise chain by extracting out some functions. Currently I have
const getData = (uuid) => {
return new Promise((resolve) => {
fetch(
// go fetch stuff
)
.then((response) => {
if (!response.ok) {
return resolve(false);
}
return response;
})
.then(fetchres.json)
.then(response => {
// Do more stuff that requires resolves that I will also want to refactor
})
.catch(err => {
console.log(err);
resolve(false);
});
});
};
So I want to extract the part where I resolve the unsuccessful responses. But pass along any successful ones. I have pulled it out like so.
const resolveUnsuccessfulResponses = (response) => {
if (!response.ok) {
return response.resolve(false);
}
return response;
}
const getData = (uuid) => {
return new Promise((resolve) => {
fetch(
// go fetch stuff
)
.then(resolveUnsuccessfulResponses)
.then(fetchres.json)
.then(response => {
// Do more stuff that requires resolves that I will also want to refactor
})
.catch(err => {
console.log(err);
resolve(false);
});
});
};
Now I'm understandably getting the error resolve is not defined. How can I resolve this Promise in an external function?
Should I pass resolve to my extracted function? That would seem clunky.
.then(response => resolveUnsuccessfulResponses(response, resolve))
I might end up having something like
.then(fetchres.json)
.then(parseResponseData)
.then(postDataSomewhere)
.then(doOtherThings)
.then(doEvenMoreCoolThings)
And to have to pass response and resolve to each of them seems wrong
You should return a new Promise from your external functions aswell:
const resolveUnsuccessfulResponses = (response) => {
return new Promise((resolve, reject) => {
if (!response.ok) {
return resolve(false);
}
return resolve(response);
});
}
Related
I'm trying to limit the number of apis fetches in my project by saving them in a simple cache, key collection in mongodb. Is thera way to stop propagation of .then() inside Promise, without using async/await?
export const getData = (url: string) => {
return new Promise((resolve, reject) => {
findInCache(url)
.then((cached: string | null) => {
if (cached) {
resolve(cached);
}
})
.then(() => {
axios
.get(url)
.then(({data}) => {
setCache(url, data, TTL);
resolve(data);
})
.catch(e => reject(e));
});
});
};
Firstly, lets get rid of the Promise constructor anti-pattern - your function call inside the promise executor returns a promise, so, no need for anew Promise
Secondly, only run the second request if the result of the first is empty
export const getData = (url) => findInCache(url)
// here we return haveResult and avoid axios.get(url) altogether
.then((haveResult) => haveResult || axios.get(url)
// sometimes nested .then is useful like in this case
.then(({data}) => {
setCache(url, data, TTL);
return data;
})
);
you can just do this instead instead of chaining. if it is in cache then fetch from cache else get from url
export const getData = (url: string) => {
return new Promise((resolve, reject) => {
findInCache(url)
.then((cached: string | null) => {
if (cached) {
resolve(cached);
} else {
axios
.get(url)
.then(({data}) => {
setCache(url, data, TTL);
resolve(data);
})
.catch(e => reject(e));
}
})
});
};
When you return something result in then, this result is come into next then function. So, you can control what you would do in next then based on input parameter inCache. So you can do something like:
export const getData = (url: string) => {
return new Promise((resolve, reject) => {
findInCache(url)
.then((cached: string | null) => {
if (cached) {
resolve(cached);
return true;
}
return false;
})
.then((inCache) => {
if (!inCache) {
axios
.get(url)
.then(({data}) => {
setCache(url, data, TTL);
resolve(data);
})
.catch(e => reject(e));
}
});
});
};
I thought I was decent at JavaScript until now. I want to write a helper function for my HTTP requests. I tested it with Jest. The problem is that the catch() part does not get triggered. Let me give you the test first:
it("recognizes when a response's status is not okay", () => {
fetch.mockResponseOnce(JSON.stringify({ ok: false }));
expect.assertions(1);
return getRequestWithoutHeader(fullTestUrl).catch(err => {
expect(err.ok).toEqual(false);
});
});
Maybe the test is written wrongly which fails. Anyways here is the helper function that I did write. I tried out different implementations and they all fail the test:
// Implementation one: with throw
export const getRequestWithoutHeader = fullUrlRoute =>
fetch(fullUrlRoute).then(response =>
response.json().then(json => {
if (!response.ok) {
throw Error(json);
}
return json;
}, error => error)
);
// Implementation two: with throw new
export const getRequestWithoutHeader = fullUrlRoute =>
fetch(fullUrlRoute).then(response =>
response.json().then(json => {
if (!response.ok) {
throw new Error(json);
}
return json;
}, error => error)
);
// Implementation three: With Promise.reject
export const getRequestWithoutHeader = fullUrlRoute =>
fetch(fullUrlRoute).then(response =>
response.json().then(json => {
if (!response.ok) {
return Promise.reject(json);
}
return json;
}, error => error)
);
// Implementation four: with new Promise
export const getRequestWithoutHeader = fullUrlRoute =>
new Promise((resolve, reject) => {
fetch(fullUrlRoute).then(response =>
response.json().then(
json => {
if (!response.ok) {
reject(json);
}
resolve(json);
},
error => reject(error)
)
);
});
None of these work. Some of these would return using a then in the test, but I want the promise to be thrown. I want to trigger a catch.
How do I have to write this helper function?
you can try like this
fetch(fullUrlRoute)
.then(response =>{
if (response.ok) {
return response.json();
}
else throw response
})
.then(json=> {
console.log(json);
})
.catch(error =>{
console.log(error)
});
Hope this help you
Here is what I ended up having to do:
I used jest-fetch-mock for mocking the requests.
In order for the promise to be correctly rejected, I had to overrite the init argument of the mockResponseOnce function.
Here is how the test ended up looking:
it("recognizes when a response's status is not okay", () => {
fetch.mockResponseOnce(JSON.stringify({ someResponse: "someResponse" }), { status: 403 });
expect.assertions(1);
return getRequestWithHeader(fullTestUrl, article).catch(err => {
expect(err.someResponse).toEqual("someResponse");
});
});
By setting the status explicitly, it automatically sets ok: false in the response, triggering the function.
I also applied CertainPerfomance's tips and refactored the function like this:
export const getRequestWithoutHeader = fullUrlRoute =>
fetch(fullUrlRoute)
.then(response => {
if (!response.ok) {
return Promise.reject(response);
}
return response.json();
})
app.js
import test from "./asyncTest";
test().then((result)=>{
//handle my result
});
asyncTest.js
const test = async cb => {
let data = await otherPromise();
let debounce = _.debounce(() => {
fetch("https://jsonplaceholder.typicode.com/posts/1")
.then( => response.json())
.then(json => json );
}, 2000);
};
export default test;
The fetch result "json" I intend to return is unable to be the return value of "test" function since the value only available in an inner function scope such as debounce wrapper. Since above reason, I tried to pass a callback function and wrap the callback to be Promise function(pTest) as below.
const test = async cb => {
let debounce = _.debounce(() => {
fetch("https://jsonplaceholder.typicode.com/posts/1")
.then(response => response.json())
.then(json => cb(null, json))
.catch(err => cb(err));
}, 2000);
};
const pTest = cb => {
return new Promise((resolve, reject) => {
test((err, data) => {
if (err) reject(err);
resolve(data);
});
});
};
export default pTest;
This way works for me, but I'm wondering if it's correct or are there any ways to solve this scenario?
The fetch API already returns a promise. Wrapping it in another Promise object is actually an anti-pattern. it is as simple as the code below:
/*export*/ async function test() {
let data = await otherPromise();
return fetch("https://jsonplaceholder.typicode.com/posts/1")
.then(response => response.json())
.then(json => {
return {
json: json,
data: data
}
});
};
function otherPromise() {
return new Promise((resolve, reject) => {
resolve('test for data value');
});
}
// In index.js call
test().then(res => {
console.log(res)
});
I'm brand new to react native and I've been browsing through snippets of code and am confused as to how promises are passed along.
I have this event handler onRefresh() that is called when I pull down on a flatlist and I'm trying to have it use the return of apiSearchDB when it returns true/false.
onRefresh = () => {
this.setState({...}, () => {
return this.apiSearchDB()
.then(function(response) {
console.log(response);
})
.catch((error) => {
console.log(error);
});
})
}
apiSearchDB = () => {
return fetch('/some_endpoint')
.then((response) => response.json())
.then((json) => {
this.setState({
...
}, () => {return true})
return true;
}).catch((error) => {
console.error(error);
return false;
})
}
The line console.log(response); only prints undefined and I can't figure out why.
Could my handler also be written as
onSearch = () => {
return new Promise((resolve, reject) => {
var response = this.apiSearchDB();
response
? resolve();
: reject();
}
});
}
or onSearch = () => {...} and function onSearch(){...}?
Thank you in advance!
You should read more about using promises (good article - We have a problem with promises). However, two basic rules that will help you in this case are are:
The value returned from a promise is wrapped in a promise.
Promises can be chained.
The apiSearchDB should return a promise that contains the json as the resolved value, and error as the rejected value:
apiSearchDB = () =>
fetch('/some_endpoint')
.then((response) => response.json())
.then((json) => json)
// can be removed unless you want to do something with the error before passing it on
.catch((error) => Promise.reject(error));
The onRefresh (or onSearch) method should get the promise from apiSearchDB, and add it's own chain. Resolve promise should be handled with the then handler. If it's the rejected value, it will be handled by the catch handler:
onRefresh = () =>
this.apiSearchDB()
.then((response) => {
console.log(response);
// do something with response
this.setState({...}, () => {
});
return response;
})
.catch((error) => {
console.log(error);
// do something with error
this.setState({...}, () => {
});
});
}
I´m trying do create a function, that returns a Promise as the code: (someprovider.ts)
postToPaymentApi(url:string, data:string, options:RequestOptions, order:Order):Promise<any>{
let result = this.http.post(url, data, options).map(res => res.json())
.subscribe(data => {
// all my logic here!
});
}, error => {
console.log(error)
})
return new Promise((resolve)=>{
resolve(result)
})
}
The problem is, when I call this function, I do not get the data, because this post take a few seconds to finish and I get the promise before the post finish.
this.postToPaymentApi(url, data, options, order).then(data => {
console.log(data);
})
What Am I doing wrong?
if you want to create a function that return promise, your function should be :
postToPaymentApi(url:string, data:string, options:RequestOptions, order:Order):Promise<any >{
return new Promise((resolve, reject) => {
this.http.post(url, data, options)
.map(res => res.json())
.subscribe(data => {
resolve(data);
}
}, error => {
console.log(error)
reject({error: error});
});
});
}
Andre, you want to use the toPromise operator to transform the observable returned by .map() into a promise. Return the result of that operator
return http.post(...).map(...).toPromise()
Now a proper promise is returned, so the calling code can use it as such:
postToPaymentApi(...).then(
data => console.log(data)
)