I am refactoring some code and removing unnecessary callbacks.
The original code was:
create(_job_id).then(function (create_id) {
latest().then(function (latest_config) {
update(create_id).then(function () {
// More code
}).catch(function (err) {
console.log(err);
});
.catch(function (err) {
console.log(err);
});
.catch(function (err) {
console.log(err);
});
I have refactored it to:
const setup = async () => {
const create_id = create(_job_id);
const latest_config = await latest();
await update(create_id);
return { create_id, latest_config };
}
setup().then((setup) => {
console.log(setup);
})
But now the rejections are not handled. How would I go about elegantly handling the "catches". Promises.all looks good but I have tried to use it unsuccessfully.
Any help is appreciated.
The non-async version could also look like this:
const p_latest_config = latest();
create(_job_id)
.then(create_id => {
update(create_id).catch(err => console.log(err));
return Promise.all([create_id, p_latest_config]);
})
.then(([create_id, latest_config]) => ({create_id, latest_config}))
.then(setup => console.log(setup))
.catch(err => console.log(err));
The above assumes that create(_job_id), latest() and update(create_id) are not depended on each other via internal mutation.
add .catch(err => {//do sth with err}) on the setup() as you do the then(). Which means it catches any of the exception returned by setup().
setup()
.then((setup) => {
console.log(setup);
})
.catch(err => {console.log("Error", err.message)})
Related
I have a simple function that calls a firestore collection and returns a list of companies. I iterate through the list and output the company name. Once it's complete, I want to write "done".
function getCompanies() {
firestore.collection('companies')
.listDocuments()
.then(companies => {
for(var i in companies) {
companies[i].get().then(company => {
console.log(company.name);
});
}
}).catch(err => {
console.error(err);
});
};
getCompanies();
console.log('done');
In reality, this is what happens...
done
ford
gm
toyota
jeep
vw
I've looked into promises...
function getCompanies() {
firestore.collection('companies')
.listDocuments()
.then(companies => {
let promises = [];
for(var i in companies) {
companies[i].get().then(company => {
promises.push(doIt(company.id));
});
}
Promise.all(promises)
.then((results) => {
console.log("All done", results);
})
.catch((e) => {
// Handle errors here
});
}).catch(err => {
console.error(err);
});
};
function doIt(value) {
return new Promise((resolve) => {
setTimeout(() => {
console.log(value);
resolve(value);
}, Math.floor(Math.random() * 1000));
});
}
getCompanies();
But that didn't work either...
All done []
ford
gm
toyota
jeep
vw
Any pointers or advice? I feel like I'm forgetting to do something obvious :/.
Thanks in advance!
That is because the console.log is synchrnous and getCompanies() is asynchrnous. The getCompanies will resolve in the future. Read up on Promise and async/await a bit.
A quick fix without using async/await will be:
function getCompanies() {
return firestore.collection('companies')
.listDocuments()
.then(companies => {
const promises = [];
for(var i in companies) {
promises.push(companies[i].get());
}
return Promise.all(promises);
}).catch(err => {
console.error(err);
});
};
Now to run the getCompanies and console.log in order, you need to execute the console.log after getCompanies resolve
getCompanies()
.then((companiesarr)=>{
companiesarr.forEach((c)=>{
console.log(c.name)
})
}).then(()=>console.log('done'))
You need to return the proimse from your function like this
function getCompanies() {
return firestore.collection('companies')
.listDocuments()
.then(companies => {
for(var i in companies) {
companies[i].get().then(company => {
console.log(company.name);
});
}
}).catch(err => {
console.error(err);
});
};
This is a great overview of promises which explains which blocks of code are asynchronous.
https://levelup.gitconnected.com/async-await-vs-promises-4fe98d11038f
If you want 'done' to print in the correct order it must be called within the async block.
If I understand correctly, .get() returns a promise so you would need to do something like this.
function getCompanies() {
firestore
.collection('companies')
.listDocuments()
.then(companies => {
const myPromisesArr = companies.map(i => i.get());
Promises.all(myPromisesArr).then(companies => {
companies.forEach(company => {
console.log(company.id);
});
});
})
.catch(error => console.error(error))
.finally(() => console.log(`done`));
}
I have async function in async function. In the second I must wait when promises resolve or reject and after run other code below. But if promise reject my code stopping and no run other functions. How I can fix it?
await axios.all(promises).then(res => {
axios.patch("/url", { foo: bar }).then(async () => {
const promises2 = arr.map(item => {
return axios.post("/url-2", item)
});
await Promise.all(promises2)
.then(() => console.log("resolved")) //this not calling ever
.catch(() => console.log("failed")) //this not calling ever
console.log("This console log ever not working")
})
})
Promises aren't chained properly, axios.patch(...) promise isn't returned. await is syntactic sugar for then and catch, its purpose is to get rid of nested functions where possible. It should be:
const res = await axios.all(promises)
await axios.patch("/url", { foo: bar })
const promises2 = arr.map(item => {
return axios.post("/url-2", item)
});
try {
await Promise.all(promises2)
console.log("resolved"))
} catch (err) {
console.log("failed");
}
The order of your code is wrong. Of course if the first promise is rejected, then the rest will not be called.
Try rewriting your code this way:
let res = await axios.all(promises).catch(() => { console.log("failed"); return false; });
if (!res) {
// Do something when rejected
....
}
// Call the 2nd promise
let res2 = await axios.path("/url", {foo: bar}).catch(() => {console.log("failed 2"); return false; });
if (!res2) {
// Do something when the 2nd promise is rejected
...
}
// Call your last promise
let res3 = await Promise.all(promises2).catch(() => {console.log("failed 3"); return false; });
if (!res3) {
// Do something if it is rejected again
....
}
// Otherwise, do your thing
Try this code, it should pinpoint where the error or rejection is occurring (i.e. it's definitely before Promise.all(promises2) is run
await axios.all(promises)
.then(res => axios.patch("/url", { foo: bar }), err => {
throw `all(promises) failed with ${err}`;
})
.then(() => {
const promises2 = arr.map(item => {
return axios.post("/url-2", item);
});
return Promise.all(promises2)
.then(() => console.log("resolved")) //this not calling ever
.catch(err => {
throw `all(promises2) failed with ${err}`;
});
}, err => {
throw `patch failed with ${err}`;
})
.catch(err => console.error(err));
Note I've removed async/await, because in the code you've posted it is totally unnecessary
I have two tables in mysql and want to query a table depending on the result of another, so I wrote a function like
export function getLocations(req, res) {
const database = new Database();
database.query('select * from districts')
.then(rows => {
let appData = [];
rows.forEach(row => {
const new_database = new Database();
new_database.query(`SELECT locations.id,locations.name, IF(subscriptions.id IS NULL,0,1) as subscribed
FROM locations
LEFT JOIN subscriptions
ON (subscriptions.location_id = locations.id AND subscriptions.user_id=1)
WHERE locations.district=?`,row.id)
.then(sub_rows => {
let district=row;
district["locations"]=sub_rows;
appData.push(district);
new_database.close();
}, err => {
return new_database.close().then(() => { throw err; })
})
.catch(err => {
console.log(err);
res.status(500).json("Database Error");
})
});
res.status(200).json(appData); //final result here
database.close()
}, err => {
return database.close().then(() => { throw err; })
})
.catch(err => {
console.log(err);
res.status(500).json("Database Error");
})
}
Here I want to get run second query based for each of the row of first query.
I am getting an empty array as result. My first query is executing properly and I logged to see all rows are being returned. What could be the issue?
You can make it work by making this async
rows.forEach(async row => {
const new_database = new Database();
await new_database.query(`SELECT locations.id,locations.name, IF(subscriptions.id IS NULL,0,1) as subscribed
FROM locations
LEFT JOIN subscriptions
ON (subscriptions.location_id = locations.id AND subscriptions.user_id=1)
WHERE locations.district=?`,row.id)
.then(sub_rows => {
let district=row;
district["locations"]=sub_rows;
appData.push(district);
new_database.close();
}, err => {
return new_database.close().then(() => { throw err; })
})
.catch(err => {
console.log(err);
res.status(500).json("Database Error");
})
});
The operation you are performing is I/O and JS is single threaded. It means in layman terms it will not wait will iterating your loop where it is going to be making a request where there is some wait while the request processes. You need to tell JS that this event is asynchronous. For this you need to use async/await
Guides
forEach async/await
async/await MDN documentation
I don't have the environment in this machine. it may have some errors but you can fix it if it have, take a look at the following logic
export function getLocations(req, res) {
const database = new Database();
const promises=[];
database.query('select * from districts')
.then(rows => {
let appData = [];
rows.forEach(row => {
promises.push(getAnotherQuery(row));
});
database.close()
}, err => {
return database.close().then(() => { throw err; })
})
.catch(err => {
console.log(err);
res.status(500).json("Database Error");
})
return Promise.all(promises).then(result)=> res.status(200).json(result); //final result here
}
getAnotherQuery=(row)=>{
return new Promise((resolve,reject)=>{
const new_database = new Database();
const appData=[]
new_database.query(`SELECT locations.id,locations.name, IF(subscriptions.id IS NULL,0,1) as subscribed
FROM locations
LEFT JOIN subscriptions
ON (subscriptions.location_id = locations.id AND subscriptions.user_id=1)
WHERE locations.district=?`,row.id)
.then(sub_rows => {
let district=row;
district["locations"]=sub_rows;
appData.push(district);
new_database.close();
resolve(appData);
}, err => {
return new_database.close().then(() => { throw err; })
})
.catch(err => {
console.log(err);
res.status(500).json("Database Error");
})
});
}
My nodejs api function:
exports.userSignup = (req, res) => {
const home = {
address: req.body.name,
phoneno: req.body.code,
};
Home.create(home)
.then((data) => {
createUser()
// i want to complete the above createUser() function fully then only i need to move to this below then function
.then(() => {
const loginDetails = {
username: 'stackoverflow',
};
User.create(loginDetails)
.then((data) => {
return res.status(200).send(data);
}).catch((err) => {
console.log('error while create schema:', err);
});
});
})
.catch((err) => {
console.log('err:', err);
});
};
My createUser function code:
const createUser = () => {
Home.findAll({
raw: true,
}).then((data) => {
data.forEach((client) => {
postgresDB.createSchema(client.code).then(() => {
Object.keys(postgresDB.models).forEach((currentItem) => {
postgresDB.models[currentItem].schema(client.code).sync();
});
console.log('Postgres schema created');
}).catch((err) => {
console.log(err);
});
});
}).catch((err) => {
console.log('Warning:', err.message);
});
};
createUser();
But this one is working as asynchronous,
How to make this using promise resolve reject or callback?
See my code, i made a comment which needs to work first,
I tried with async await but not working!
In order for promise chaining to work, you have to return a promise for any asynchronous functions or else it won't wait. You're also making another asynchronous call for each client you iterate through. In order to deal with multiple promises at once, you need to push each promise into an array and pass it to Promise.all. Here's a snippet that should work.
const createUser = () => {
return Home.findAll({
raw: true,
}).then((data) => {
const promises = [];
data.forEach((client) => {
promises.push(
postgresDB.createSchema(client.code).then(() => {
Object.keys(postgresDB.models).forEach((currentItem) => {
postgresDB.models[currentItem].schema(client.code).sync();
console.log('Postgres schema created');
})
})
);
});
return Promise.all(promises);
}).catch((err) => {
console.log('Warning:', err.message);
});
};
The problem lies in the synchronous data.forEach() call inside the createUser function which doesn't wait for the async createSchema calls to finish before returning.
You can fix this by using an async implementation of forEach function or by using Promise.all()
Here's a piece of code that might work for you:
const createUser = () => {
return Home.findAll({
raw: true
}).then((data) => Promise.all(
data.map((client) => postgresDB.createSchema(client.code).then(() =>
Promise.all(Object.keys(postgresDB.models).map((currentItem) =>
postgresDB.models[currentItem].schema(client.code).sync()
))
))
))
.catch((err) => {
console.log('Warning:', err.message);
});
};
If async/await doesn't quite work for you, just use promise chaining. It's super painful to see people just write callback based code with promises and forget that you can chain promises by returning a promise from the onFulfilled() parameter to then():
const createUser = () => {
return Home.findAll({ raw: true }).
then(data => {
return Promise.all(data.map(client => postgresDB.createSchema(client.code)));
}).
then(() => {
const keys = Object.keys(postgresDBModels);
return Promise.all(keys.map(currentItem => {
return postgresDB.models[currentItem].schema(client.code).sync();
}));
}).
then(() => console.log('Postgres schema created')).
catch((err) => {
console.log(err);
});
};
createUser();
You can use async and await in createUser function. You can alter your function by Using promise, We can able to either resolve or reject.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
Please save my sanity. I'm trying to do some error handling inside my observable. I've followed heaps of articles but it hasn't clicked. I'd rather handle errors inside the service and push out the cached data if an error occurs.
I do this inside my angular2 component...
private initializeJobPolling() {
this.subscription = Observable
.interval(5000)
.startWith(0)
.flatMap(() => {
return this.jobService.getJobs();
})
.subscribe(
(jobsContainer: any) => {
let allJobs: IJob[] = jobsContainer.jobs.map(j => new Job(j));
this.allJobs = allJobs;
} );
}
and inside jobService...
getJobs() {
let thisService = this;
return thisService.http.get('app/services/jobs.json')
.map((responseData) => {
console.log('getting Jobs from API');
thisService.allJobs = responseData.json();
return thisService.allJobs;
});
}
Use catch:
getJobs() {
return this.http.get('app/services/jobs.json')
.map((responseData) => {
console.log('getting Jobs from API');
this.allJobs = responseData.json();
return this.allJobs;
})
.catch(err => {
console.error(err);
return Observable.just(this.allJobs);
});
}
Update:
As pointed out in the comments, RxJS 5 doesn't have .just, so use .of instead:
getJobs() {
return this.http.get('app/services/jobs.json')
.map((responseData) => {
console.log('getting Jobs from API');
this.allJobs = responseData.json();
return this.allJobs;
})
.catch(err => {
console.error(err);
return Observable.of(this.allJobs);
});
}