I have a method like this:
doSomeRequests(listOfRequestParameters) {
let requests = listOfRequestParameters.map(parmeter => {
return axios.get(url + parmeter)
.then(data => {
const parameters= data.parameters;
return axios.delete(url, parameters)
})
})
return Promise.all(requests);
}
In this function I want to complete API requests for each element in a list (listOfRequestParameters), but for every element in the list I have to do two API requests.
The axios methods both return Promises.
The problem is that I do catch the result of the Promise returned y doSomeRequests method, but if one API requests fail I get an UnhandledPromiseRejectionWarning.
What am I missing? Is there a way to do this kind of Promise chain?
Thanks to #charlietfl for asking me if I really do doSomeRequests(..).then().catch(). Turns out I don't, I accidentally did doSomeRequests(..).then().then() but was somehow convinced the I need to search for the error in the "complex" Promise chain.
Anyway, I am sorry for the useless question, thank you for your help.
You need to make sure the map call returns a list of promises, simplifying with async/await you'll get something along the lines:
async doSomeRequests(listOfRequestParameters) {
return Promise.all(listOfRequestParameters.map(async parmeter => {
const data = await axios.get(url + parameter);
const parameters = data.parameters;
return axios.delete(url, parameters);
}));
}
await doSomeRequests(listOfRequestParameters);
Related
I have this code, wherein i'd like to make it in single array.
The output that data produce, is like this:
connections.elements.map((val: any) => {
const url = 'link'
return new Promise((resolve) => {
axios.post(url, val.firstName).then((res: { data: any }) => {
resolve(searchRequestBuilder(res.data.AllResults));
});
});
});
const searchRequestBuilder = async (data: any) => {
console.log(await data);
// for await (let resolvedPromise of data) {
// console.log(resolvedPromise);
// }
};
What I'd like to do is like this:
I already tried to make a variable and use .push, but it still doesn't combine in a single array. What was the thing I am missing?
So since your .map function will return an array of promises, you can store that in a variable, and you can use Promise.all or Promise.allSettled to wait for all the apis to resolve and you can get all the results in a single array in the order that it is requested.
Not part of the question, another thing is if you are not careful, and use await on each api request, it can make a waterfall pattern of requests, which means it will wait for API 1 to finish, then API 2 to finish.
But the way its written here, it will make all the requests parallely, so this will request all the APIs together, and once the last one gets resolved the Promise.all or allSettled will trigger its callback.
const searchRequestBuilder = async (data: any) => {
console.log(await data);
}
async function makeAPIrequests(){
let arrrayOfPromises = connections.elements.map((val: any) => {
const url = 'link'
return new Promise((resolve) => {
axios.post(url, val.firstName).then((res: { data: any }) => {
resolve(searchRequestBuilder(res.data.AllResults));
});
});
});
Promise.allSettled(arrrayOfPromises).
then((results) => console.log(results))
}
Edit:
Also in addition to this, I do not think that you need to return a new Promise inside the map funciton, if you just return axios it should work, since axios itself will return a promise.
The main problem there could be you are making a new call per each item of your elements array. This may create performance issues. The best practice may be to do only one async call and then get all items.
Anyway, if for some reason you really need to make all these ajax calls. Think you are working with asynchronous code, then you really don't know when all calls are answered. To fix this, you need to call your searchRequestBuilder after had made all calls.
I suggest you use Promise.all method.
I left you this link to show you how it works. Basically what you need to do is save all axios.post promises in an array and after loop your elements array call Promises.all passing the array with your promises, then you will be able to execute your searchRequestBuilder without problems.
You can try using .concat:
let allData = [];
allData = allData.concat(await data);
I'm fairly new to JavaScript, and I'm finding the concept of promises and "then" statements really hard to pin down. I'm working on a website using Firebase, and I'm trying to get user data from the database. Here's what I have at the moment:
// Function that fetches a user's data from the database.
getUserData = function(uid) {
var database = firebase.firestore();
var docRef = database.collection('users').doc(uid);
var userData = docRef.get().then(function(doc) {
if (doc.exists) {
return doc.data();
} else {
console.log("User does not exist.");
return null;
}
}).catch(function(error) {
console.log("Error getting document:", error);
});
return userData;
}
Currently, this function returns a Promise object. When I log the object to the console, it says that the promise status is resolved and its value is the value of doc.data(). What I really want is to pass the value of doc.data() back up the "chain" to the getUserData function so that I can return and use it elsewhere. Is there a simple way to do this? I've tried to find a way to get the "value" of a Promise out of the object itself, but I've found nothing, and so far I have failed to find any other way to do what I want.
I'm sure it's a lot simpler than it seems, and I feel like an idiot for asking this sort of thing, but I've been searching for a couple of hours and none of the explanations I've found are helping. I would greatly appreciate any advice. Thanks!
Edit: Solved! Thanks for the help, everyone! Just in case this helps someone in the future who stumbles upon it, I'm leaving my final (working) code here. Unfortunately there's no way to call it from a synchronous function, but it ended up being good enough for my purposes:
// Function that fetches a user's data from the database.
async function getUserData(uid) {
var database = firebase.firestore();
var docRef = database.collection('users').doc(uid);
var doc = await docRef.get();
if (doc.exists) {
return doc.data();
} else {
console.log("User does not exist.");
return null;
}
}
First you must understand that Promise are mainly used for asynchronous operation, and they do not store the value UNTIL the asynchronous operation is resolved.
Promises to not expose any "get" method to retrieve directly their value but only a then method to execute a callback once the promise is resolved. Thus, with Promise you always need to "wait" for it to have resolved to read the value.
Now, you have two choices :
The first is to use the old way of then() and catch().
You make your function return a Promise (like you did) and where you call your function, you can use then to execute a callback with your result.
getUserData("anId").then((userData) => console.log('the async result is' + userData))
You can also use catch to handle the error :
getUserData("anId")
.then((userData) => console.log('the async result is' + userData))
.catch((error) => console.error('wooopsie : ' + error))
As for the "new" way of doing things, you can use the async and await keywords of JS to handle your Promises.
Basically, a function declared async will always return a Promise and will define a context allowing to use the await keyword. And await will allow you to write your Promise handling just like synchronous code.
async function(){
const userData = await getUserData("anId");
console.log('the async result is' + userData);
Finally, if you need error handling, you will need to wrap the await instruction with a try-catch.
Hope this helps.
The reason for using promises is that they make asynchronous code easier to work with. Unfortunately the code can still be a little tricky if you're not used to it. You're probably better off using async/await in most situations as it will be easier to work with and also probably easier to understand:
async function getUserData(uid) {
var database = firebase.firestore();
var docRef = database.collection('users').doc(uid);
var doc = await docRef.get();
if(doc.exists)
return doc.data();
return null;
}
This is how you would use it:
// code to execute before
getUserData(uid).then(userData => {
if(userData) {
// code to execute after
}
else console.log("User does not exist.");
}).catch(error => {
console.log("Error getting document:", error);
});
Alternatively (Async/Await):
async function doSomething(uid) {
// code to execute before
const userData = await getUserData(uid);
// code to execute after
}
Async/Await with custom error handling:
async function doSomething(uid) {
// code to execute before
try {
const userData = await getUserData(uid);
if(userData) {
// code to execute after
}
else console.log("User does not exist.");
}
catch(error) {
console.log("Error:", error);
}
}
To get value from promise you need to use .then, so in your case to get data in the line you call getUserData, you need to use same construct (getUserData.then((data) => {}))
Background
I started working at a company that doesn't have many patterns and I see the return of fetch calls in two ways. One returning just the fetch call and another returning a fetch call wrapped by Promise using resolve and reject, and this made me get confused.
Question
Is doing this
const returnFetchWrappedByPromise = () => {
return new Promise((resolve, reject) => {
return fetch("url")
.then(resolve(true))
.catch(reject(false))
})
}
The same as this
const returnFetch = () => {
return fetch("url")
.then(() => true)
.catch(() => false)
}
?
If not, what is the difference?
If yes, which one should I use?
Observation: I used true and false just to make an example, but in reality, is some result and some error.
The new Promise object within the returnFetchWrappedByPromise function is not needed. fetch already returns a Promise object (Screenshot below). You should use the returnFetch function. Hope that helps.
fetch inherently returns a promise, so wrapping it in a new Promise doesn't add any functionality. The use of async also does not add anything in this case and actually presents a case of the Promise constructor anti-pattern.
The second function syntax is the preferred way.
I understand that this is a basic question, but I can't figure it out myself, how to export my variable "X" (which is actually a JSON object) out of "for" cycle. I have tried a various ways, but in my case function return not the JSON.object itself, but a "promise.pending".
I guess that someone more expirienced with this will help me out. My code:
for (let i = 0; i < server.length; i++) {
const fetch = require("node-fetch");
const url = ''+(server[i].name)+'';
const getData = async url => {
try {
const response = await fetch(url);
return await response.json();
} catch (error) {
console.log(error);
}
};
getData(url).then(function(result) { //promise.pending w/o .then
let x = result; //here is real JSON that I want to export
});
}
console.log(x); // -element is not exported :(
Here's some cleaner ES6 code you may wish to try:
const fetch = require("node-fetch");
Promise.all(
server.map((srv) => {
const url = String(srv.name);
return fetch(url)
.then((response) => response.json())
.catch((err) => console.log(err));
})
)
.then((results) => {
console.log(results);
})
.catch((err) => {
console.log('total failure!');
console.log(err);
});
How does it work?
Using Array.map, it transforms the list of servers into a list of promises which are executed in parallel. Each promise does two things:
fetch the URL
extract JSON response
If either step fails, that one promise rejects, which will then cause the whole series to reject immediately.
Why do I think this is better than the accepted answer? In a word, it's cleaner. It doesn't mix explicit promises with async/await, which can make asynchronous logic muddier than necessary. It doesn't import the fetch library on every loop iteration. It converts the server URL to a string explicitly, rather than relying on implicit coercion. It doesn't create unnecessary variables, and it avoids the needless for loop.
Whether you accept it or not, I offer it up as another view on the same problem, solved in what I think is a maximally elegant and clear way.
Why is this so hard? Why is async work so counterintuitive?
Doing async work requires being comfortable with something known as "continuation passing style." An asynchronous task is, by definition, non-blocking -- program execution does not wait for the task to complete before moving to the next statement. But we often do async work because subsequent statements require data that is not yet available. Thus, we have the callback function, then the Promise, and now async/await. The first two solve the problem with a mechanism that allows you to provide "packages" of work to do once an asynchronous task is complete -- "continuations," where execution will resume once some condition obtains. There is absolutely no difference between a boring node-style callback function and the .then of a Promise: both accept functions, and both will execute those functions at specific times and with specific data. The key job of the callback function is to act as a receptacle for data about the asynchronous task.
This pattern complicates not only basic variable scoping, which was your main concern, but also the issue of how best to express complicated workflows, which are often a mix of blocking and non-blocking statements. If doing async work requires providing lots of "continuations" in the form of functions, then we know that doing this work will be a constant battle against the proliferation of a million little functions, a million things needing names that must be unique and clear. This is a problem that cannot be solved with a library. It requires adapting one's style to the changed terrain.
The less your feet touch the ground, the better. :)
Javascript builds on the concept of promises. When you ask getData to to do its work, what is says is that, "OK, this is going to take some time, but I promise that I'll let you know after the work is done. So please have faith on my promise, I'll let you know once the work is complete", and it immediately gives you a promise to you.
That's what you see as promise.pending. It's pending because it is not completed yet. Now you should register a certain task (or function) with that promise for getData to call when he completes the work.
function doSomething(){
var promiseArray = [];
for (let i = 0; i < server.length; i++) {
const fetch = require("node-fetch");
const url = ''+(server[i].name)+'';
const getData = async url => {
try {
const response = await fetch(url);
return await response.json();
} catch (error) {
console.log(error);
}
};
promiseArray.push(getData(url)); // keeping track of all promises
}
return Promise.all(promiseArray); //see, I'm not registering anything to promise, I'm passing it to the consumer
}
function successCallback(result) {
console.log("It succeeded with " + result);
}
function failureCallback(error) {
console.log("It failed with " + error);
}
let promise = doSomething(); // do something is the function that does all the logic in that for loop and getData
promise.then(successCallback, failureCallback);
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 5 years ago.
I have an es6 class, with an init() method responsible for fetching data, transforming it, then update the class's property this.data with newly transformed data.
So far so good.
The class itself has another getPostById() method, to just do what it sounds like. Here is the code for the class:
class Posts {
constructor(url) {
this.ready = false
this.data = {}
this.url = url
}
async init() {
try {
let res = await fetch( this.url )
if (res.ok) {
let data = await res.json()
// Do bunch of transformation stuff here
this.data = data
this.ready = true
return data
}
}
catch (e) {
console.log(e)
}
}
getPostById(id){
return this.data.find( p => p.id === id )
}
}
Straightforward, except I have an async/await mechanism in the init() method.
Now, this code will work correctly:
let allPosts = new Posts('https://jsonplaceholder.typicode.com/posts')
allPosts.init()
.then( d => console.log(allPosts.getPostById(4)) )
// resulting Object correctly logged in console
but it only gets printed into the console:
How could I use allPosts.getPostById(4) as a return of a function ?
Like:
let myFunc = async () => {
const postId = 4
await allPosts.init() // I need to wait for this to finish before returning
// This is logging correct value
console.log( 'logging: ' + JSON.stringify(allPosts.getPostById( postId ), null, 4) )
// How can I return the RESULT of allPosts.getPostById( postId ) ???
return allPosts.getPostById( postId )
}
myFunc() returns a Promise but not the final value. I have read several related posts on the subject but they all give example of logging, never returning.
Here is a fiddle that includes two ways of handling init(): using Promise and using async/await. No matter what I try, I can't manage to USE the FINAL VALUE of getPostById(id).
The question of this post is: how can I create a function that will RETURN the VALUE of getPostById(id) ?
EDIT:
A lot of good answers trying to explain what Promises are in regards to the main execution loop.
After a lot of videos and other good reads, here is what I understand now:
my function init() correctly returns. However, within the main event loop: it returns a Promise, then it is my job to catch the result of this Promise from within a kinda parallel loop (not a new real thread). In order to catch the result from the parallel loop there are two ways:
use .then( value => doSomethingWithMy(value) )
use let value = await myAsyncFn(). Now here is the foolish hiccup:
await can only be used within an async function :p
thus itself returning a Promise, usable with await which should be embed in an async function, which will be usable with await etc...
This means we cannot really WAIT for a Promise: instead we should catch parallel loop indefinitely: using .then() or async/await.
Thanks for the help !
As for your comment; I'll add it as answer.
The code you write in JavaScript is run on one thread, that means that if your code could actually wait for something it will block any of your other code from getting executed. The event loop of JavaScript is explained very well in this video and if you like to read in this page.
A good example of blocking code in the browser is alert("cannot do anything until you click ok");. Alert blocks everything, the user can't even scroll or click on anything in the page and your code also blocks from executing.
Promise.resolve(22)
.then(x=>alert("blocking")||"Hello World")
.then(
x=>console.log(
"does not resolve untill you click ok on the alert:",
x
)
);
Run that in a console and you see what I mean by blocking.
This creates a problem when you want to do something that takes time. In other frameworks you'd use a thread or processes but there is no such thing in JavaScript (technically there is with web worker and fork in node but that's another story and usually far more complicated than using async api's).
So when you want to make a http request you can use fetch but fetch takes some time to finish and your function should not block (has to return something as fast as possible). This is why fetch returns a promise.
Note that fetch is implemented by browser/node and does run in another thread, only code you write runs in one thread so starting a lot of promises that only run code you write will not speed up anything but calling native async api's in parallel will.
Before promises async code used callbacks or would return an observable object (like XmlHttpRequest) but let's cover promises since you can convert the more traditional code to a promise anyway.
A promise is an object that has a then function (and a bunch of stuff that is sugar for then but does the same), this function takes 2 parameters.
Resolve handler: A function that will be called by the promise when the promise resolves (has no errors and is finished). The function will be passed one argument with the resolve value (for http requests this usually is the response).
Reject handler: A function that will be called by the promise when the promise rejects (has an error). This function will be passed one argument, this is usually the error or reason for rejection (can be a string, number or anything).
Converting callback to promise.
The traditional api's (especially nodejs api's) use callbacks:
traditionalApi(
arg
,function callback(err,value){
err ? handleFail(err) : processValue(value);
}
);
This makes it difficult for the programmer to catch errors or handle the return value in a linear way (from top to bottom). It gets even more impossible to try and do things parallel or throttled parallel with error handling (impossible to read).
You can convert traditional api's to promises with new Promise
const apiAsPromise = arg =>
new Promise(
(resolve,reject)=>
traditionalApi(
arg,
(err,val) => (err) ? reject(err) : resolve(val)
)
)
async await
This is what's called syntax sugar for promises. It makes promise consuming functions look more traditional and easier to read. That is if you like to write traditional code, I would argue that composing small functions is much easier to read. For example, can you guess what this does?:
const handleSearch = search =>
compose([
showLoading,
makeSearchRequest,
processRespose,
hideLoading
])(search)
.then(
undefined,//don't care about the resolve
compose([
showError,
hideLoading
])
);
Anayway; enough ranting. The important part is to understand that async await doesn't actually start another thread, async functions always return a promise and await doesn't actually block or wait. It's syntax sugar for someFn().then(result=>...,error=>...) and looks like:
async someMethod = () =>
//syntax sugar for:
//return someFn().then(result=>...,error=>...)
try{
const result = await someFn();
...
}catch(error){
...
}
}
The examples allways show try catch but you don't need to do that, for example:
var alwaysReject = async () => { throw "Always returns rejected promise"; };
alwaysReject()
.then(
x=>console.log("never happens, doesn't resolve")
,err=>console.warn("got rejected:",err)
);
Any error thrown or await returning a rejected promise will cause the async function to return a rejected promise (unless you try and catch it). Many times it is desirable to just let it fail and have the caller handle errors.
Catching errors could be needed when you want the promise to succeed with a special value for rejected promises so you can handle it later but the promise does not technically reject so will always resolve.
An example is Promise.all, this takes an array of promises and returns a new promise that resolves to an array of resolved values or reject when any one of them rejects. You may just want to get the results of all promises back and filter out the rejected ones:
const Fail = function(details){this.details=details;},
isFail = item => (item && item.constructor)===Fail;
Promise.all(
urls.map(//map array of urls to array of promises that don't reject
url =>
fetch(url)
.then(
undefined,//do not handle resolve yet
//when you handle the reject this ".then" will return
// a promise that RESOLVES to the value returned below (new Fail([url,err]))
err=>new Fail([url,err])
)
)
)
.then(
responses => {
console.log("failed requests:");
console.log(
responses.filter(//only Fail type
isFail
)
);
console.log("resolved requests:");
console.log(
responses.filter(//anything not Fail type
response=>!isFail(response)
)
);
}
);
Your question and the comments suggest you could use a little intuition nudge about the way the event loop works. It really is confusing at first, but after a while it becomes second nature.
Rather than thinking about the FINAL VALUE, think about the fact that you have a single thread and you can't stop it — so you want the FUTURE VALUE -- the value on the next or some future event loop. Everything you write that is not asynchronous is going to happen almost immediately — functions return with some value or undefined immediately. There's nothing you can do about. When you need something asynchronously, you need to setup a system that is ready to deal with the async values when they return sometime in the future. This is what events, callbacks, promises (and async/await) all try to help with. If some data is asynchronous, you simply can not use it in the same event loop.
So what do you do?
If you want a pattern where you create an instance, call init() and then some function that further process it, you simply need to setup a system that does the processing when the data arrives. There are a lot of ways to do this. Here's one way that's a variation on your class:
function someAsync() {
console.log("someAsync called")
return new Promise(resolve => {
setTimeout(() => resolve(Math.random()), 1000)
})
}
class Posts {
constructor(url) {
this.ready = false
this.data = "uninitilized"
this.url = url
}
init() {
this.data = someAsync()
}
time100() {
// it's important to return the promise here
return this.data.then(d => d * 100)
}
}
let p = new Posts()
p.init()
processData(p)
// called twice to illustrate point
processData(p)
async function processData(posts) {
let p = await posts.time100()
console.log("randomin * 100:", p)
}
init() saves the promise returned from someAsync(). someAsync() could be anything that returns a promise. It saves the promise in an instance property. Now you can call then() or use async/await to get the value. It will either immediately return the value if the promise has already resolved or it will deal with it when it has resolved. I called processData(p) twice just to illustrate that it doesn't calle the someAsync() twice.
That's just one pattern. There are a lot more — using events, observables, just using then() directly, or even callbacks which are unfashionable, but still can be useful.
NOTE: Wherever you use await it has to be inside an async function.
Check out the UPDATED FIDDLE
You need to use await myFunc() to get the value you expect from getPostById because an async function always returns a promise.
This sometimes is very frustrating as the whole chain needs to be converted into async functions but that's the price you pay for converting it to a synchronous code, I guess. I am not sure if that can be avoided but am interested in hearing from people who have more experience on this.
Try out the below code in your console by copying over the functions and then accessing final and await final.
NOTE:
An async function CAN contain an await expression.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
There is no rule that is must have await in order to even declare an async function.
The example below uses an async function without await just to show that an async function always returns a promise.
const sample = async () => {
return 100;
}
// sample() WILL RETURN A PROMISE AND NOT 100
// await sample() WILL RETURN 100
const init = async (num) => {
return new Promise((resolve, reject) => {
resolve(num);
});
}
const myFunc = async (num) => {
const k = await init(num);
return k;
}
// const final = myFunc();
// final; This returns a promise
// await final; This returns the number you provided to myFunc