One of the promises is not being resolved, what can be the issue possibly?
const items = await Promise.all(data.map(async i => {
const tokenUri = await contract.tokenURI(i).catch(function (error) {return;});
if (tokenUri.length < 8) {
return;
}
let url = "http://127.0.0.1:5001/api/v0/get?arg=" + tokenUri.slice(7)
const meta = await axios.post(url).catch(function (error) {return;});
success++;
}), function (err) {
callback(err);
})
This part of the code is misplaced:
, function (err) {
callback(err);
})
Currently, it is being passed as a second argument to Promise.all(). Promise.all() only takes one argument (an iterable like an array) so that's being ignored and that callback will NEVER get called.
If you meant for that to be like the second argument to .then(), then you'd have to have a .then() to use it with, but you're trying to use it as second argument to Promise.all() and that is not correct.
If you want to know when Promise.all() is done, you would do this:
try {
const items = await Promise.all(data.map(async i => {
const tokenUri = await contract.tokenURI(i).catch(function(error) { return; });
if (tokenUri.length < 8) {
return;
}
let url = "http://127.0.0.1:5001/api/v0/get?arg=" + tokenUri.slice(7)
const meta = await axios.post(url).catch(function(error) { return; });
success++;
}));
console.log("Promise.all() is done", items);
} catch (e) {
console.log(e);
}
Note, you are silently "eating" errors with no logging of the error inside this loop which is pretty much never a good idea.
If, as you propose, one of your promises is actually never being resolved/rejected, then none of this code will do anything to fix that and you will have to go down to the core operation and find out why it's not resolving/rejecting on its own. Or, configure a timeout for it (either in the API or by manually creating your own timeout).
Related
The code below is an example of what may take place during development.
With the current code, the outer function may throw an error but in this case wont. However, the nested function WILL throw an error (for examples sake). Once it throws the error it cannot be caught as it is asynchronous function.
Bungie.Get('/Platform/Destiny2/Manifest/').then((ResponseText)=>{
//Async function that WILL throw an error
Bungie.Get('/Platform/Destiny2/Mnifest/').then((ResponseText)=>{
console.log('Success')
})
}).catch((error)=>{
//Catch all errors from either the main function or the nested function
doSomethingWithError(error)
});
What I want is for the outer most function to catch all asynchronous function error's but with this code I cannot. I have tried awaiting the nested function but there may be certain circumstances where it will be quicker to not wait for the function. I also tried to include a .catch() with each nested function but this would require a .catch() for each function that would allhandle the error in the same way e.g. doSomethingWithError().
you only needs return the inner function in the outside function.
see example below:
const foo = new Promise((resolve,reject) =>{
setTimeout(() => resolve('foo'), 1000);
});
foo.then((res)=>{
console.log(res)
return new Promise((resolve,reject)=>{
setTimeout(() => reject("bar fail"), 1000);
})
}).catch((e)=>{
// your own logic
console.error(e)
});
this is called promise chaining. see this post for more info https://javascript.info/promise-chaining
if you have multiple promises can do something like:
const foo1 = new Promise((resolve,reject) =>{
setTimeout(() => resolve('foo1'), 1000);
});
const foo2 = new Promise((resolve,reject) =>{
setTimeout(() => resolve('foo2'), 2000);
});
const foo3 = new Promise((resolve,reject) =>{
setTimeout(() => reject('foo3'), 3000);
});
const bar = new Promise((resolve,reject) =>{
setTimeout(() => resolve('bar'), 4000);
});
foo1
.then((res)=>{
console.log(res)
return foo2
})
.then((res)=>{
console.log(res)
return foo3 // throws the error
})
.then((res)=>{
console.log(res)
return bar
})
.catch((e)=>{
// every error will be cached here
console.error(e)
});
I would aim to use async / await unless you have very particular reasons, since it avoids callback hell and makes your code simpler and more bug free.
try {
const response1 = await Bungie.Get('/Platform/Destiny2/Manifest/');
const response2 = await Bungie.Get('/Platform/Destiny2/Mnifest/');
console.log('Success');
} catch (error) {
doSomethingWithError(error);
}
Imagine each Bungie call takes 250 milliseconds. While this is occurring, NodeJS will continue to execute other code via its event loop - eg requests from other clients. Awaiting is not the same as hanging the app.
Similarly, this type of code is used in many browser or mobile apps, and they remain responsive to the end user during I/O. I use the async await programming model in all languages these days (Javascript, Java, C#, Swift etc).
Try this:
let getMultiple = function(callback, ... keys){
let result = [];
let ctr = keys.length;
for(let i=0;i<ctr;i++)
result.push(0);
let ctr2 = 0;
keys.forEach(function(key){
let ctr3=ctr2++;
try{
Bungie.Get(key, function(data){
result[ctr3] = data;
ctr--;
if(ctr==0)
{
callback(result);
}
});
} catch(err) {
result[ctr3]=err.message;
ctr--;
if(ctr==0)
{
callback(result);
}
}
});
};
This should get all your data requests and replace relevant data with error message if it happens.
getMultiple(function(results){
console.log(results);
}, string1, string2, string3);
If the error causes by requesting same thing twice asynchronously, then you can add an asynchronous caching layer before this request.
I've been tinkering with this for a few days now and have seen a number of different patterns. In some ways I feel more confused than I did when I began!
itemsArr is a list of item objects (itemObj) with summary information about each item. Each itemObj contains an itemId which doubles as the API slug directory. So, I need to iterate through the itemsArr, make an API call for each item, and return the updated array with all of the details that were retrieved from each API call. When this is finished, I want to log the enriched array, enrichedItemsArr to persistant storage.
It does not matter in what order the API calls return, hence using async.each. I also don't want to interrupt the execution if an error occurs. My questions:
'Done enriching array' is printing before execution of enrichArr() -> why is await async.each... in enrichArr() not blocking?
I am getting TypeError: callback is not a function in the inner try-catch. Not sure why.
If I pass err to callback() in the inner try-catch, will that halt execution?
Should I pass itemsArr to processDone as the 2nd argument? Is there a way to return itemsArr to main() from the processDone() method?
Does err passed to the final callback contain an array of errors?
const main = async () => {
const itemsArr = items.getArr(); // --> retrieves locally cached itemsArr
const enrichedItemsArr = await enrichArr(itemsArr); // --> handling the async iterator stuff below
await logToDB(enrichedItemsArr); // --> helper function to log enriched info to database
console.log('Done enriching array');
};
const enrichArr = async (itemsArr) => {
// Outer try-catch
try {
const processItem = async (item, callback) => {
// Inner try-catch
try {
const res = await doSomethingAsync(itemID);
item.res = res;
callback(); // --> currently getting `TypeError: callback is not a function`
} catch (err) {
item.err = err;
callback(err); // --> not 100% sure what passing err here does...
}
};
const processDone = (err, itemsArr) => {
if (err) console.error(err); // --> Is err an array of errors or something?
return itemsArr; // --> how do I return this to main()?
};
await async.each(itemsArr, processItem, processDone);
} catch (err) {
throw err; // --> if async.each errors, throw
}
};
Hope this is a good answer for you.
why is await async.each... in enrichArr() not blocking?
Based on the docs, the async.each will return a promise only if the callback is omitted
each
You're including the callback, the async.each won't return a promise and won't block your code using async/await
I am getting TypeError: callback is not a function in the inner try-catch. Not sure why.
Your processItem should be a plain function, doing that I was able to use callback, seems that the library is not happy when you use async functions
const processItem = (item, callback) => {
const itemId = item;
// Inner try-catch
try {
doSomethingAsync((res) => {
item.res = res;
callback()
});
} catch (err) {
item.err = err;
callback(err); // --> not 100% sure what passing err here does...
}
};
If I pass err to callback() in the inner try-catch, will that halt execution?
Yes, it will throw an error
Should I pass itemsArr to processDone as the 2nd argument? Is there a way to return itemsArr to main() from the processDone() method?
If you want to let know the main method that needs to wait, you won't be able to use processDone.
ItemsArr is an object, you can mutate the object and the main method should be able to see those changes, there is no other way if you want to use array.each.
Maybe there is another method in the async library that allows you to return a new array.
Maybe Map is a good option map
Does err passed to the final callback contain an array of errors?
No, it's a way to let the library know that needs to throw an error.
I created a snippet to allow you to play with the code
const async = require('async');
const logToDB = async (items) => {
items.forEach((item) => console.log(JSON.stringify(item)))
}
const doSomethingAsync = (callback) => {
setTimeout(() => {
console.log('processing data')
callback()
}, 1000);
}
const main = async () => {
const itemsArr = [
{
itemId: '71b13422-2582-4975-93c9-447b66764daf'
},
// {
// errorFlag: true
// },
{
itemId: '8ad24197-7d30-4514-bf00-8068e216e90c'
}
]; // --> retrieves locally cached itemsArr
const enrichedItemsArr = await enrichArr(itemsArr); // --> handling the async iterator stuff below
await logToDB(enrichedItemsArr); // --> helper function to log enriched info to database
console.log('Done enriching array');
};
const enrichArr = async (itemsArr) => {
// Outer try-catch
try {
const processItem = (item, callback) => {
console.log('item: ', item);
const itemId = item;
// Inner try-catch
try {
if (item.errorFlag) {
return callback('Test error');
}
doSomethingAsync((res) => {
item.res = res;
callback()
});
} catch (err) {
item.err = err;
callback(err); // --> not 100% sure what passing err here does...
}
};
await async.each(itemsArr, processItem);
return itemsArr;
} catch (err) {
console.log('Error occurred');
throw err; // --> if async.each errors, throw
}
};
main();
I am using q and I have multiple mongoose .exec() promises that never gets to the .then() part of the code, so never allow the q to resolve. Can't figure out why it never comes back.
var defer = q.defer();
var promises = [];
console.log('Exams:', exams.length);
for (var e=0; e<exams.length; e++) {
console.log('Exams:', exams[e]._id);
var newPromise = Pupilexam.find({ _exam: exams[e]._id }).populate('_user').exec()
.then((pupils) => {
console.log("Adding pupils", exams[e]._id);
exams[e].pupils = pupils;
resolve(exams[e]);
})
.catch((err) => {
reject(err);
});
console.log(typeof newPromise);
promises.push(newPromise);
console.log("Promised pushed");
}
q.all(promises).then(function(data){
console.log("q'd all");
defer.resolve(res.status(200).json(exams));
});
return defer;
The Pupilexam.find().exec() never reaches the .then() so the promises never resolve and the defer never resolves. Why would the mongoose find not get to the .then()? What have I missed?
*** UPDATE ***
Even using the built in promises, we get the same issue. The Pupilexams.find() call never comes back.
var promises = [];
for (var e=0; e<exams.length; e++) {
console.log('e:', e);
console.log('Exam', exams[e]._id);
var newPromise = Pupilexam.find({ _exam: exams[e]._id }).populate('_user').exec()
.then((pupils) => {
console.log("Adding pupils", exams[e]._id);
exams[e].pupils = pupils;
})
.catch(handleError(res));
promises.push(newPromise);
}
Promise.all(promises).then((exams) => {
console.log(values);
res.status(200).json(exams)
});
With this method I also get a headers error on the call UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
** ADDITIONAL CODE REQUESTED **
function handleError(res, statusCode) {
statusCode = statusCode || 500;
return function(err) {
console.log(err.message);
res.status(statusCode).send(err);
};
}
To answer the updated question regarding the Cannot set headers after they are sent to the client error. Looks like you send a response to the client inside your handleError function. Now, if more than one Pupilexam.find call fails, handleError would be invoked twice, resulting in the mentioned error.
You should move the catch-handler down to the Promise.all call:
const promises = [];
for (const exam of exams) {
const newPromise = Pupilexam
.find({ _exam: exam._id }).populate('_user').exec()
.then((pupils) => {
exam.pupils = pupils;
});
promises.push(newPromise);
}
Promise.all(promises)
.then((exams) => {
res.status(200).json(exams);
})
.catch(handleError(res));
I guess that you are indeed returning your promise but you are returning an empty json.
There are 2 problems with your approach:
You are not returning from your then: should return pupils and it is returning undefined
You are logging values that I don't know what it is
.then((pupils) => {
console.log("Adding pupils", exams[e]._id);
exams[e].pupils = pupils;
// you should return something // return pupils
})
promises.push(newPromise);
Promise.all(promises).then((exams) => {
// ['undefined', 'undefined', ...]
console.log(values);
res.status(200).json(exams)
});
Looks like the answer was that on these two lines the exams[e] is not in scope, because by the time the promise comes back the loop has moved on, so e is wrong and gets too high so it was erroring.
console.log("Adding pupils", exams[e]._id);
exams[e].pupils = pupils;
Only discovered that when I read #eol's message about the catch and decided to catch it properly and output.
it is Look from your code.
//(async) function
for (var e of exams) {
try {
const pupils = await Pupilexam.find({ _exam: exams[e]._id
}).populate('_user').exec().lean()
e.pupils = pupils
}catch((err){
//handleError
};
}
res.status(200).json({data: exams})
maybe that will show you how match are you wrong
I am developing a server project which needs to call some functions synchronously. Currently I am calling it in asynchronous nature. I found some similar questions on StackOverflow and I can't understand how to apply those solutions to my code. Yet I tried using async/await and ended up with an error The 'await' operator can only be used in an 'async' function
Here is my implementation
function findSuitableRoom(_lecturer, _sessionDay, _sessionTime, _sessionDuration, _sessionType){
let assignedRoom = selectRoomByLevel(preferredRooms, lecturer.level, _sessionDuration); <------- Need to be call synchronously
if (!assignedRoom.success){
let rooms = getRooms(_sessionType); <------- Need to be call synchronously
assignedRoom = assignRoom(_lecturer.rooms, _sessionDuration, _lecturer.level);
} else {
arr_RemovedSessions.push(assignedRoom.removed)
}
return assignedRoom;
}
function getRooms(type){
switch (type){
case 'Tutorial' : type = 'Lecture hall'
break;
case 'Practical' : type = 'Lab'
break;
default : type = 'Lecture hall'
}
Rooms.find({type : type},
(err, rooms) => {
if (!err){
console.log('retrieved rooms ' + rooms)
return rooms;
}
})
}
Here I have provided only two methods because full implementation is very long and I feel if I could understand how to apply synchronous way to one method, I can manage the rest of the methods. Can someone please help me?
Well yes await is only available inside an async function so put async infront of findSuitableRoom.
Also you did a classic mistake. You use return inside of a callback function, and expect getRooms to return you some value.
async function findSuitableRoom(
_lecturer,
_sessionDay,
_sessionTime,
_sessionDuration,
_sessionType
) {
let assignedRoom = selectRoomByLevel(
preferredRooms,
lecturer.level,
_sessionDuration
);
if (!assignedRoom.success) {
try {
let rooms = await getRooms(_sessionType);
} catch (err) {
console.log("no rooms found");
}
assignedRoom = assignRoom(
_lecturer.rooms,
_sessionDuration,
_lecturer.level
);
} else {
arr_RemovedSessions.push(assignedRoom.removed);
}
return assignedRoom;
}
Also wrap it in an try / catch
Since .find() returns an promise if you dont pass an callback you can write it like this
function getRooms(type) {
switch (type) {
case "Tutorial":
type = "Lecture hall";
break;
case "Practical":
type = "Lab";
break;
default:
type = "Lecture hall";
}
return Rooms.find({ type });
}
Note here findSuitableRoom is no longer synchronouse. Its async and returns an promise. That means you will need to use the function like this:
findSuitableRoom.then(res => { console.log(res); })
The 'await' operator can only be used in an 'async' function
This means whenever you want to use the await keyword it needs to be inside a function which has an async keyword (returns promise)
read this https://javascript.info/async-await for more info
const add = (a, b) => {
return new Promise((resolve, reject) => {
setTimeout(() => { //to make it asynchronous
if (a < 0 || b < 0) {
return reject("don't need negative");
}
resolve(a + b);
}, 2000);
});
};
const jatin = async () => {
try{
const sum = await add(10, 5);
const sum2 = await add(sum, -100);
const sum3 = await add(sum2, 1000);
return sum3;
} catch (e) {
console.log(e);
}
};
jatin()
Try this
let's understand this
add is a normal function that does some asynchronous action like
waiting for 2 seconds
normally we use await with async function so in order to use it we make as async function jatin and use await with add function call
to make it synchronous, so until first await add call() doesn't
happen it wont execute another await add call().
Example code if you will use in your app.js
router.post("/users/login", async (req, res) => {
try {
const user = await User.findByCredentials(
req.body.email,
req.body.password
);
const token = await user.generateToken();
res.status(200).send({
user,
token,
});
}
catch (error) {
console.log(error);
res.status(400).send();
}
});
I would like to have a configuration file with variables set with data I fetch from an API.
I think I must use async and await features to do so, otherwise my variable would stay undefined.
But I don't know how to integrate this and keep the node exports.myVariable = myData available within an async function ?
Below is the code I tried to write to do so (all in the same file) :
const fetchAPI = function(jsonQuery) {
return new Promise(function (resolve, reject) {
var reqOptions = {
headers: apiHeaders,
json:jsonQuery,
}
request.post(apiURL, function (error, res, body) {
if (!error && res.statusCode == 200) {
resolve(body);
} else {
reject(error);
}
});
});
}
var wallsData = {}
const fetchWalls = async function (){
var jsonQuery = [{ "recordType": "page","query": "pageTemplate = 1011"}]
let body = await utils.fetchAPI(jsonQuery)
let pageList = await body[0].dataHashes
for(i=0;i<pageList.length;i++){
var page = pageList[i]
wallsData[page.title.fr] = [page.difficultyList,page.wallType]
}
return wallsData
throw new Error("WOOPS")
}
try{
const wallsData = fetchWalls()
console.log(wallsData)
exports.wallsData = wallsData
}catch(err){
console.log(err)
}
The output of console.log(wallsData) shows Promise { <pending> }, therefore it is not resolved and the configuration file keep being executed without the data in wallsData...
What do I miss ?
Thanks,
Cheers
A promise is a special object that either succeeds with a result or fails with a rejection. The async-await-syntax is syntactic sugar to help to deal with promises.
If you define a function as aync it always will return a promise.
Even a function like that reads like
const foo = async() => {
return "hello";
}
returns a promise of a string, not only a string. And you need to wait until it's been resolved or rejected.
It's analogue to:
const foo = async() => {
return Promise.resolve("Hello");
}
or:
const foo = async() => {
return new Promise(resolve => resolve("Hello"));
}
Your fetchWalls similarly is a promise that will remain pending for a time. You'll have to make sure it either succeeds or fails by setting up the then or catch handlers in your outer scope:
fetchWalls()
.then(console.log)
.catch(console.error);
The outer scope is never async, so you cannot use await there. You can only use await inside other async functions.
I would also not use your try-catch for that outer scope promise handling. I think you are confusing the try-catch approach that is intended to be used within async functions, as there it helps to avoid nesting and reads like synchronous code:
E.g. you could do inside your fetchWalls defintion:
const fetchWalls = async function (){
var jsonQuery = [{ "recordType": "page","query": "pageTemplate = 1011"}]
try {
let body = await utils.fetchAPI(jsonQuery)
} catch(e) {
// e is the reason of the promise rejection if you want to decide what to do based on it. If you would not catch it, the rejection would chain through to the first error handler.
}
...
}
Can you change the statements like,
try{
const wallsData = fetchWalls();
wallsData.then((result) => {
console.log(result);
});
exports.wallsData = wallsData; // when importing in other file this returns as promise and we should use async/await to handle this.
}catch(err){
console.log(err)
}