Returning Promise in JavaScript es6 - javascript

Let's say I have a file that is importing a function from the api and doing something with the result.
import api from './api'
const getData = () => {
api.get(url)
.then(res => console.log(res))
.catch(err => console.log(err))
}
I've seen two different styles for returning the response so the error bubbles up.
version 1:
get: (url) => {
return new Promise((resolve, reject) => axios.get(url)
.then(res => resolve(res))
.catch(err => reject(err)) )
}
version 2:
get: (url) => {
return axios.get(url)
.then(res => {
if (res.success == 200) {
return Promise.resolve(res)
}
return Promise.reject(res)
})
}
What are the differences between the two approaches? Is there a preferred/better method for error handling?

In general, two handy rules:
If your starting point is a promise (the return value of axios.get), then using new Promise is an anti-pattern. then and catch already create new promises.
Propagate errors, don't convert them to resolutions (until, of course, the point at which your handling those errors, usually near the beginning of the call chain).
Based on that, of those two options, you'd use Version 2, not Version 1.
Version 1 is a bit nonsensical: It eats the resolution value and converts errors to resolutions; it will always resolve to undefined. Remember that the result of the chain is the result of the last thing returned (or thrown) in a then or catch handler. Those two handlers don't return or throw anything, so the chain resolves (rather than rejecting) with undefined.
Version 2 does something: It modifies the promise result based on res.success. But it doesn't need Promise.resolve, it should just be:
get: (url) => axios.get(url).then(res => {
if (res.success == 200) {
return res;
}
return Promise.reject(res); // Or see note below
})
And there's a camp — which I agree with — which says you should always throw an Error object rather than returning Promise.reject, for stack trace info. So:
get: (url) => axios.get(url).then(res => {
if (res.success == 200) {
return res;
}
throw new Error(res/*...or something else useful here...*/);
})

There is no real difference with the two versions, at the first one, you are creating a redundant Promise.
axios.get, is a function that returns a Promise there is no need to wrap it with another one.
You can warp with additional Promise if you wanna change the promise logic, meaning that if you wanna resolve no matter of what happened in axios promise.
Something like that:
get: (url) => {
return new Promise((resolve, reject) => axios.get(url)
.then(res => resolve(res))
.catch(err => resolve(err)) )
}
but, usually it is not the practice.

Related

Can't call fetch api multiple times

I want to call this api multiple times in my project and when I am calling it , It continues giving an error which is
TypeError: Failed to execute 'json' on 'Response': body stream already
read at main.js:Line number
My Code is as Follows
let thisIsUrl = 'https://api.covid19api.com/summary';
let a = fetch(thisIsUrl)
a.then((data) => {
return data.json()
}).then((apidata) => {
console.log(apidata)
return apidata
}).catch((error) => {
console.log(error)
})
a.then((fetchdata) => {
return fetchdata.json()
}).then((readingData) => {
console.log(readingData)
}).catch((err) => {
console.log(err)
})
You're not calling fetch multiple times. You're calling it once, and then trying to read the response body multiple times. That's why the error says you're trying to read the body when the stream is already closed — it was closed when you were done reading it the first time.
If you want to use the data twice, store it somewhere and use it twice.
let thisIsUrl = 'https://api.covid19api.com/summary';
let a = fetch(thisIsUrl)
a.then((data) => {
return data.json()
}).then((apidata) => {
// **************** Use it twice here
}).catch((error) => {
console.log(error)
})
If you want to fetch it again because it may have been updated, call fetch again:
let thisIsUrl = 'https://api.covid19api.com/summary';
fetch(thisIsUrl)
.then((data) => {
return data.json();
}).then((apidata) => {
console.log(apidata);
return apidata;
}).catch((error) => {
console.log(error);
});
// Presumably this is later (in time), not immediately after the above
fetch(thisIsUrl)
.then((fetchdata) => {
return fetchdata.json();
}).then((readingData) => {
console.log(readingData);
}).catch((err) => {
console.log(err);
});
Finally, this seems unlikely, but if you really want to fetch it once and use that one result in multiple places via the promise chain, keep the promise from then rather than the promise from fetch:
let thisIsUrl = 'https://api.covid19api.com/summary';
let a = fetch(thisIsUrl)
.then((data) => {
return data.json()
});
a.then((apidata) => {
// ***** Use it here
}).catch((error) => {
console.log(error)
})
a.then((readingData) => {
// ***** And then again here
}).catch((err) => {
console.log(err);
});
Side note: Your code is falling prey to a footgun in the fetch API; I've written about it in this blog post. fetch only rejects its promise on network errors, not HTTP errors. You have to check for those yourself in the first fulfillment handler, by checking for ok on the response object:
fetch("/your/resource")
.then(response => {
if (!response.ok) {
throw new Error("HTTP error " + response.status); // Or better, use an Error subclass
}
return response.json();
})
// ...
fetch returns Promise, generally, promises have something like state inside themself;
pending: initial state, neither fulfilled nor rejected.
fulfilled: meaning that the operation was completed successfully.
rejected: meaning that the operation failed.
(source)
So when we call them and get the value from them with then, catch and etc. then they change the state after that call. So here, when you read the value with a.then(…, the promise changes its state to fulfilled and you are not able to call it again, you need a new and fresh Promise, actually a new instance of the fetch.
I want to recommend you to use Promise.all().
let thisIsUrl = 'https://api.covid19api.com/summary';
let a = fetch(thisIsUrl)
.then((data) => {
return data.json()
}).then((apidata) => {
console.log(apidata)
return apidata
}).catch((error) => {
console.log(error)
})
Promise.all([a,a,a]);
.then(results => {
// Results are here.
});

Can you add a .then to a promise after it's created?

Promises just baffle me.
I'm trying to make a mock data service to imitate axios.
My mock put call passes a targetUrl to _fetch which then sees if it's a valid url and either returns a new Promise with a delayed .resolve
const _returnResponse = (mockData, time = 0) => new Promise((resolve) => {
setTimeout(() => {
resolve(mockData);
}, time);
});
or a new Promise with a delayed .reject
const _returnError = (time = simulatedDelay) => {
const returnValue = new Promise(((resolve, reject) => {
setTimeout(() => {
reject(new Error('error'));
}, time);
}));
return returnValue;
};
but when I make my mock put call this returns a mock data that the calling method interprets as a success and console logs in its .then
put(target, putBody) {
const returnValue = _fetch(target, simulatedDelay)
returnValue.then(response => _console('PUT', target, response, putBody));
return returnValue;
},
But with an invalid target console logs an uncaught error
or this handles the error correctly, but console logs an undefined response
put(target, putBody) {
const returnValue = _fetch(target, simulatedDelay).then(response => _console('PUT', target, response, putBody));
return returnValue;
},
Here's the calling method:
saveStuff({ commit, state }, newStuff) {
//other code
return this.$mockAxios.put(url, putBody)
.then((response) => {
return response;
});
},
I feel like I'm completely missing something and I've researched this for hours and I'm still not getting it.
As a direct answer to the question: yes, you can add .then() to a promise after it's created.
Example:
const hi = new Promise((resolve, reject) => {
setTimeout(() => resolve('hello'), 2000);
});
hi.then(result => console.log(result));
As for promises baffling you, I would recommend (aside from more reading) just playing around a lot in your IDE with setTimeout. I know you're already using setTimeout in your mock, but strip it down further and just run the code in its own file, so that you control the whole environment. With lots of console.log('blah') to see the order and such. Also ensure you're just as familiar with callbacks, as promises are just syntactic sugar for callbacks. Try to read up on the JS event loop at the same time - it might provide some context if you know how and when a callback/promise executes.
Note that you can even add .then() after the callback has resolved or rejected. Hence the term "promise" - it's literally a promise that your .then() function will run. https://en.wikipedia.org/wiki/Promise_theory
const hi = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('one second');
resolve();
}, 1000);
});
setTimeout(() => {
hi.then(() => console.log('two seconds. this executes approximately a full second after the first promise has resolved'));
}, 2000);

JavaScript Promise inside async/await function resolve final array of responses

I'm quite a newbie in JavaScript and in Promises.
I'm trying to build an array of objects that I get from an API.
To do so, I've build two functions in a file MyFile.js.
The first one returns a promise when an axios promise is resolved. It's
function get_items (url) {
return new Promise((resolve, reject) => {
let options = {
baseURL: url,
method: 'get'
}
axios(options)
.then(response => {
resolve(response.data)
})
.catch(error => {
reject(error.stack)
})
})
}
The second one looks like this:
let output = []
let next_url = 'https://some_url.com/api/data'
async function get_data () {
try {
let promise = new Promise((resolve, reject) => {
if (next_url) {
get_items(next_url)
.then(response => {
output.push(...response.results)
if (response.next) {
next_url = response.next
console.log('NEXT_URL HERE', next_url)
get_data()
} else {
console.log('else')
next_url = false
get_data()
}
})
.catch(error => {
reject(error.stack)
})
} else {
console.log('before resolve')
resolve(output)
}
})
return await promise
} catch(e) {
console.log(e)
}
}
It's where I'm grinding my teeth.
What I think I understand of this function, is that:
it's returning the value of a promise (that's what I understand return await promise is doing)
it's a recursive function. So, if there is a next_url, the function continues on. But if there is not, it gets called one last time to go into the else part where it resolves the array output which contains the results (values not state) of all the promises. At least, when I execute it, and check for my sanity checks with the console.log I wrote, it works.
So, output is filled with data and that's great.
But, when I call this function from another file MyOtherFile.js, like this:
final_output = []
MyFile.get_data()
.then(result => {
console.log('getting data')
final_output.push(...result)
})
it never gets into the then part. And when I console.log MyFile.get_data(), it's a pending promise.
So, what I would like to do, is be able to make get_data() wait for all the promises result (without using Promise.all(), to have calls in serie, not in parallel, that would be great for performances, I guess?) and then be able to retrieve that response in the then part when calling this function from anywhere else.
Keep in mind that I'm really a newbie in promises and JavaScript in general (I'm more of a Python guy).
Let me know if my question isn't clear enough.
I've been scratching my head for two days now and it feels like I'm running in circle.
Thanks for being an awesome community!
This is a bit untested
const api_url = 'https://some_url.com/api/data';
get_data(api_url).then((results) => {
console.log(results);
}).catch((error) => {
// console.error(error);
});
function get_items (url) {
const options = {
baseURL: url,
method: 'get'
};
return axios(options).then((response) => response.data);
}
async function get_data(next_url) {
const output = [];
while (next_url) {
const { results, next } = await get_items(next_url);
output.push(...results);
next_url = next;
}
return output;
}
Basically it makes things a bit neater. I suggest to look at more examples with Promises and the advantage and when to ease await/async. One thing to keep in mind, if you return a Promise, it will follow the entire then chain, and it will always return a Promise with a value of the last then.. if that makes sense :)
There are a few problems. One is that you never resolve the initial Promise unless the else block is entered. Another is that you should return the recursive get_data call every time, so that it can be properly chained with the initial Promise. You may also consider avoiding the explicit promise construction antipattern - get_items already returns a Promise, so there's no need to construct another one (same for the inside of get_items, axios calls return Promises too).
You might consider a plain while loop, reassigning the next_url string until it's falsey:
function get_items (baseURL) {
const options = {
baseURL: url,
method: 'get'
}
// return the axios call, handle errors in the consumer instead:
return axios(options)
.then(res => res.data)
}
async function get_data() {
const output = []
let next_url = 'https://some_url.com/api/data'
try {
while (next_url) {
const response = await get_items(next_url);
output.push(...response.results)
next_url = response.next;
}
} catch (e) {
// handle errors *here*, perhaps
console.log(e)
}
return output;
}
Note that .catch will result in a Promise being converted from a rejected Promise to a resolved one - you don't want to .catch everywhere, because that will make it difficult for the caller to detect errors.
Another way of doing it is to not use async at all and just recursively return a promise:
const getItems = (url) =>
axios({
baseURL: url,
method: 'get',
}).then((response) => response.data);
const getData = (initialUrl) => {
const recur = (result, nextUrl) =>
!nextUrl
? Promise.resolve(result)
: getItems(nextUrl).then((data) =>
recur(result.concat([data.results]), data.next),
);
return recur([],initialUrl)
.catch(e=>Promise.reject(e.stack));//reject with error stack
};
As CertainPerformance noted; you don't need to catch at every level, if you want getData to reject with error.stack you only need to catch it once.
However; if you had 100 next urls and 99 of them were fine but only the last one failed would you like to reject in a way that keeps the results so far so you can try again?
If you do then the code could look something like this:
const getData = (initialUrl) => {
const recur = (result, nextUrl) =>
!nextUrl
? Promise.resolve(result)
: getItems(nextUrl)
.catch(e=>Promise.reject([e,result]))//reject with error and result so far
.then((data) =>
recur(result.concat([data.results]), data.next),
);
return recur([],initialUrl);//do not catch here, just let it reject with error and result
};

Best approach in passing promise's returned value to many functions

Let a function which returns a promise:
async function foo() {
return await new Promise((res, rej) => {
fetch('https://jsonplaceholder.typicode.com/todos/1')
.then(res => res.json())
.then(data => res(data))
.catch(err => rej(err))
})
}
In order to reuse the returned data I can think something like:
(async function() {
data = await foo().catch(err => console.log('err: ', err))
fnc1(data)
fnc2(data)
...
fncn(data)
})();
or something like:
foo().then(data => {
fnc1(data)
fnc2(data)
...
fncn(data)
}
)
So always I have to go back in my code find the function or callback which gets the data returned by the promise and include any new function that I want to the appropriate block.
I was wondering if there is any smarter way to achieve the same result in javascript.
I know that something like the following won't work:
var dataFromPromise
foo().then(data => dataFromPromise = data)
console.log(dataFromPromise) //undefined if called before foo runs
 Don't wrap promises inside promises
fetch already returns a Promise, so there is no need to wrap it into another Promise. Instead of:
async function foo() {
return await new Promise((res, rej) => {
fetch('https://jsonplaceholder.typicode.com/todos/1')
.then(res => res.json())
.then(data => res(data))
})
}
Do:
function foo(){
return fetch('https://jsonplaceholder.typicode.com/todos/1')
.then(res => res.json())
.then(data => res(data))
.catch(err => rej(err))
}
 Reusing promises
Once you have a Promise, should it be pending, rejected or fulfilled, you can reuse it again and again, and if it is resolved it will always give the returned value. So instead of:
foo().then(data => {
fnc1(data)
fnc2(data)
...
fncn(data)
}
)
You can just do:
const myPromise = foo();
myPromise.then(data => fnc1(data));
// some code
myPromise.then(data => fnc2(data));
// more code
myPromise.then(data => fnc3(data));
And you can be sure that fnc1 and the others won't get call until the promise resolves.
Now, this sure is good, but doesn't solve all the related problems, and a lot of bad things can still happen with this approach. To tackle all the possible ways to handle and reuse promise is something too wide for a SO answer.
A pretty good resource about this topic is:
http://2ality.com/2017/08/promise-callback-data-flow.html
Store the promise once somewhere:
const dataPromise = foo();
Then whenever you need to call a function that uses it:
dataPromise.then(fnc1)
Or inside async functions:
fnc1(await dataPromise)

Returning an Axios Promise from function

Can someone please explain why returning an Axios promise allows for further chaining, but returning after applying a then()/catch() method does not?
Example:
const url = 'https://58f58f38c9deb71200ceece2.mockapi.io/Mapss'
function createRequest1() {
const request = axios.get(url)
request
.then(result => console.log('(1) Inside result:', result))
.catch(error => console.error('(1) Inside error:', error))
return request
}
function createRequest2() {
const request = axios.get(url)
return request
.then(result => console.log('(2) Inside result:', result))
.catch(error => console.error('(2) Inside error:', error))
}
createRequest1()
.then(result => console.log('(1) Outside result:', result))
.catch(error => console.error('(1) Outside error:', error))
createRequest2()
.then(result => console.log('(2) Outside result:', result))
.catch(error => console.error('(2) Outside error:', error))
<script src="https://unpkg.com/axios#0.16.1/dist/axios.min.js"></script>
https://jsfiddle.net/nandastone/81zdvodv/1/
I understand that Promise methods should return a value to be chained, but why is there a difference between these two return methods?
Your first example returns the original promise. Your second example returns a different promise, the one created by calling catch.
The critical differences between the two are:
In your second example, you're not passing on the resolution value, so the promise returned by your then is resolved with undefined (the return value of console.log).
In your second example, you're converting rejections into resolutions with undefined (by returning the result of console.log out of catch). A catch handler that doesn't throw or return a promise that's rejected converts a rejection into a resolution.
One of the key things about promise chains is that they transform the result; every call to then or catch creates a new promise, and their handlers can modify what's sent downstream as the result passes through them.
The usual pattern would indeed be to return the result of the chain, but for the functions in the chain to either intentionally transform the result or pass it on. Normally, you wouldn't have a catch handler except at the terminal end of the chain, unless you're using it to correct the error condition (intentionally converting a rejection into a resolution).
If you wanted to just log what passed through while still allowing callers to see it but did want to return the result of the chain for whatever reason, you'd do this:
return request
.then(result => { console.log(result); return result; })
.catch(error => { console.error(error); return Promise.reject(error); });
or using throw:
return request
.then(result => { console.log(result); return result; })
.catch(error => { console.error(error); throw error; });

Categories