There's a chain of promises, the first takes user's id from data base after i need to get his groups and names of constructor from data base
And these can be multiple copies
As a result cycle into cycle. eventually I receive object only of one user
async(takeIds()
.then(ids => {
// console.log("ids", ids);
for (var i = 0; i < ids.length; i++) {
data.id = ids[i];
await (takeIdvk(ids[i])
.then(idm => {
data.idvk = idm;
takeIdg(data.id)
.then(res => {
takeNamesCycle(res, data)
.then(data => {
console.log("data", data);
})
.catch(err => {
console.log(err);
});
})
.catch(err => {
console.log(err);
});
})
.catch(err => {
console.log(err);
}));
}
})
.catch(function(err) {
console.log(err);
}));
And function takeNamesCycle cos it differs from other
( in the rest simple requests to data base)
var takeNamesCycle = async(function(arr, obj) {
return new Promise((resolve, reject) => {
for (var i = 0; i < arr.length; i++) {
var idg = arr[i];
await (takeNames(arr[i])
.then(names => {
obj.idg[idg] = names;
})
.catch(err => {
console.log(err);
}));
}
resolve(obj);
});
});
Is it possible to simplify this?
I'd be grateful
Consider using real async/await syntax instead of a library with async() and await() functions.
const ids = await takeIds();
// console.log("ids", ids);
try {
for (var i = 0; i < ids.length; i++) {
data.id = ids[i];
const idem = await takeIdvk(ids[i]);
data.idvk = idm;
const res = await takeIdg(data.id);
console.log("data", await takeNamesCycle(res, data));
}
} catch(err) {
console.log(err);
}
and make sure to avoid the Promise constructor antipattern
async function takeNamesCycle(arr, obj) {
for (const idg of arr) {
const names = await takeNames(idg);
obj.idg[idg] = names;
}
return obj;
}
The posted code is mixing promise chain style of code within await and async functions within bad code.
First await does not permit .then or .catch methods to be called on the value it returns. The reason is that the value returned by await is never a promise. await only resumes execution after a successful promise operation and returns the result of the operation. So for example
await (takeIdvk(ids[i])
.then(idm => { // incorrect
data.idvk = idm;
// ... more code
becomes
let idm = await takeIdvk(ids[i]) // await returns fulfilled value
data.idvk = idm;
// ... more code
In other words, code to handle data for a successful operation is written inline after await instead of in a separate onfulfilled handler passed as a parameter to .then.
Promise Rejections
If the promise being waited on becomes rejected, the await operator throws the rejection reason without resuming function execution.
The rejection can be caught in a try/catch block in order to ignore the error, repeat the operation, or replace it with a different operation as determined by application requirements. E.G.
let idm;
try {
idm = await takeIdvk(ids[i]) // await returns fulfilled value
data.idvk = idm;
// ... more code
} catch( err) {
console.log( "ignoring error + err);
// the value of idm is undefined
}
On the other hand, if rejection is to be considered fatal don't put await inside a try/catch block - the thrown value will be caught and used to reject the promise returned by the async function, much like throwing an error inside a then or catch handler.
If calls to async functions are nested, each called using await without any try/catch blocks, rejection errors will bubble up from deeply nested functions and reject the promise returned by the outermost async function call. A single .catch clause on the outermost promise can be used to prevent uncaught promise rejections and notify that a failure occurred.
Conclusion
Try writing the code using plain promise chains if you are more familiar with that syntax. By all means continue with converting it to async/await syntax but be aware that async/await is an alternative syntax and they don't mix well in the same function. As already stated by #Bergi , returning a new promise from an async function is an anti pattern - async functions return a promise already.
Related
I found that I cannot resolve the promises one by one.
This is my code
const promises = mkt.map((marketItem) => {
return context.program.account.chain
.fetch(marketItem[0].instrument)
.then((instrumentRes) => {
return Test(context, marketItem[0]).then(
testResult => {
return Promise.all([
functionA(),
functionB(),
]).then((result) => {
return result
});
}
);
});
});
console.log(promises)
for (let i=0; i < promises.length; i++) {
const val = await promises[i]();
console.log(val);
}
error
promises[i]() is not a function
Why is that?How can I solve it?
promises[i]() is not a function
Correct, this is because promises is an array of Promise objects, not functions.
You can dump this promises array into Promise.all and wait for them all to resolve.
Promise.all(promises)
.then(resolvedPromises => {
// handle array of resolved promises
});
but I am working on some real time update stuff. If I use Promise.all
it will throw error Too many request, so I want to do it one by one to
see the effect
For this then I think you want to iterate on the mkt array and wait for each created Promise to resolve. Refactor the mapping callback into a standalone function that you can manually invoke within the for-loop, awaiting the the Promise to settle.
const request = (marketItem) => {
return context.program.account.chain
.fetch(marketItem[0].instrument)
.then((instrumentRes) => Test(context, marketItem[0]))
.then(testResult => Promise.all([functionA(), functionB()]))
.then((result) => result);
}
for (let i=0; i < mkt.length; i++) {
try {
const val = await request(mkt[i]);
console.log(val);
} catch(error) {
// handle any error
}
}
Just removing the () from the await call should already work.
const val = await promises[i];
However keep in mind that all the promises have already been "started" (I don't know a better word for that) at that point and you're just retrieving the results or an error with the await call.
I'm new to promises, so apologize for the newbie question. Inside my function, I have the following lines (middle of the function):
...
const retPromise = await buildImgs(file, imgArray);
retPromise.then(async function () {
console.log("completed build imgs");
...
My assumption was that the "then" from the promise would not execute until the await was completed. but alas, it is acting like sync code, and the retPromise evaluating the "then" before the buildImgs is completed (as measured by my console.log flows). The result is an undefined retPromise.
please help...what am I missing in the concept?
OK: after feedback, let me explaing further my question:
I am trying to understand this async/sync flow and control concept:
const retVal = somefunc();
console.log(retVal);
const retVal = await somefunc();
console.log(retVal);
in the first case, if I understand sync / async code correctly, then I should have a possibility that retVal is undefined when the console.log finds it...
in the second case, I thought it would stop flow until that point that somefunc() completes, then the flow would continue. However my reading seems to indicate it will still try to run the console.log message as a parallel thread. So this leads me to believe I would need to put the console.log inside of the .then after somefunc. Which leads me to promises. So I made a promise return, which I see happening.
However, the .then, as in my original post code, seems to post the console message "completed build imgs", before code inside my buildImgs completes (measured by time I know the function to take, and also console messages inside the buildImgs to help me with sequencing)
so it seems to me I am still missing a fundamental on how to block flow for async code. :(
When you use await construction the script waits until the promise resolves and return to your retPromise value from this promise.
So in this case better to choose one. Remove await and keep then, or keep await and use retPromise value.
Assuming that buildImgs is actually returning a promise (example)
const buildImgs = (file, imgArray) => {
return new Promise((resolve, reject) => {
try {
// ...
resolve()
} catch (err) {
reject(err)
}
})
}
When you call await on a promise its already waiting for the promise to complete, if you remove the await on the call then you can use .then
there are two ways to write promise handlers, the older way looks like this
buildImgs(file, imgArray)
.then(() => {
console.log('im done')
})
.catch(err => {
console.error(err)
})
and the newer syntax
// you must declare "async" in order to use "await"
const asyncFunction = async () => {
try {
await buildImgs(file, imgArray)
console.log('im done')
} catch(err) {
console.error(err)
}
}
asyncFunction()
if you need the return value from the promise, then you would assign it to a variable
const ineedResponse = await buildImgs(file, imgArray)
console.log(ineedResponse)
// or
buildImgs(file, imgArray)
.then(ineedResponse => {
console.log(ineedResponse)
})
.catch(err => {
console.error(err)
})
I have the below async function taking in an array of 4 objects. The first one is processed in a .map loop that executes a query and results in an error (in the await executeQuery(queryString)). When this first object is completed processing, the loop ends and the other 3 objects are not processed.
async function myFunction(arrayOfObjects, param1) {
const promises = arrayOfObjects.map(async function (currElement, i) {
var queryString = 'www.querystring.com?APIparameter=param1';
const executeQuery = util.promisify(request);
await executeQuery(queryString).then(data => {
//process data here using currElement, i
}).catch(err => console.log('error: ', err));
});
await Promise.all(promises).catch();
}
I originally only had await Promise.all(promises) and thought revising it to await Promise.all(promises).catch(); would do the trick but it is still failing out on the first object iterated.
I was wondering how best to achieve continuing the .map loop after an error hits the catch in the executeQuery.
Please and thank you!
Not 100% sure what's happening with this code but there are a few things I would change. Firstly it's not good practice to use an await inside a loop and secondly, your promises array is probably not what you expect as the await is yielding until each promise is resolved inside the loop. Return the Promise from the map function to generate an array of Promises that will each resolve and process the data in their own time. You can then await all the promises and catch any errors using try/catch. Something similar to the following should do what you are expecting...
async function myFunction(arrayOfObjects, param1) {
const executeQuery = util.promisify(request);
const promises = arrayOfObjects.map((currElement, i) => {
const queryString = 'www.querystring.com?APIparameter=param1';
return executeQuery(queryString).then(data => {
// process data here using currElement, i
});
});
try {
await Promise.all(promises);
// all promises will be resolved here
} catch (err) {
console.log('error: ', err);
}
}
I'm struggling to get my head around this. I've searched for similar problems.
Basically, I have 2 asynchronous functions. Let's call them getData() and processData() for the purposes of this question, which both return a promise. (these 2 functions are not defined using the async keyword)
I then have a function which calls these 2 using the async keyword. Like so:
async function init() {
// get the data
for (var i = 0; i < arr_process.length; i++) {
try {
await getData(arr_process[i]);
} catch (err) {
return Promise.reject(err);
}
}
// now process the data
for (var i = 0; i < arr_done.length; i++) {
try {
await processData(arr_done[i]);
} catch (err) {
return Promise.reject(err);
}
}
}
My question is; is this the correct way to handle promise rejections. My understanding is as soon as you define a function using the async keyword it will return a promise. I want to reject that promise if either of the functions using the await keyword (getData or processData) reject a promise - and I don't want the remainder of the function to execute. So if the getData promise rejects above, I don't want the loop to continue or the second loop to start - I want to exit the function returning a rejected promise. Is this the correct implementation of what I'm after? Hopefully that all makes sense!
All the try/catches are unnecessary. If a Promise that is awaited rejects, the whole containing async function will reject, and the rejection value will be the thrown error. You may simply do:
async function init() {
// get the data
for (var i = 0; i < arr_process.length; i++) {
await getData(arr_process[i]);
}
// now process the data
for (var i = 0; i < arr_done.length; i++) {
await processData(arr_done[i]);
}
}
and then, just like with your original code, the caller of init will catch any errors with a catch:
init()
.catch((err) => {
console.log('error:', err);
});
You can throw an error from within an async function by simply using the throw keyword.
For example:
async function init() {
throw new Error('error message')
}
or in your case, just remove the try catch blocks and any errors will be thrown automatically.
async function init() {
// get the data
for (var i = 0; i < arr_process.length; i++) {
await getData(arr_process[i]);
}
// now process the data
for (var i = 0; i < arr_done.length; i++) {
await processData(arr_done[i]);
}
}
New to concept of callbacks and promises. I'm trying to read contents of a directory which is correctly returning the addresses but this code is only printing console.log(value) and the console.log in the function getAccount "gettin info..." but nowhere printing the balance the api is getting closed and the process completes, I dont understand this because the address is being passed and the first console.log is printing inside the function but not doing further job. when i remove the fs.readdir and files.foreach and pass a single value to getAccount its working perfectly fine. No errors or bugs , its a runtime error and im guessing related to callbacks
'use strict';
const RippleAPI = require('ripple-lib').RippleAPI;
const testFolder = '/home/ripple/.keystore';
const fs = require('fs');
const api = new RippleAPI({server: 'wss://s1.ripple.com'});
function getAccount(address) {
var balance = 0.0;
console.log("getting info for :"+address);
return api.getAccountInfo(address).then(info => {
var balance = info.xrpBalance;
console.log("balance of "+address+" is "+balance);
return balance;
});
}
api.connect().then(() => {
fs.readdir(testFolder, function(err, files) {
// console.log("Total no of wallets : "+files.length);
// for (var i =3000, len=3200; i < len; i++) {
// var ad = files[i];
// console.log(ad + "\t" + typeof(ad));
// if(!xwallets.includes(ad))
files.forEach(function(value) {
getAccount(value).then(balance => {console.log(balance); });
console.log(value);
});
// console.log("running for index : "+i+" filedata :"+files[i]);
// }
});
// console.log(balance);
}).then(() => {
return api.disconnect();
}).then(() => {
console.log("done and disconnected");
}).catch(console.error);
You really don't want to mix asynchronous operations that return a promise with those that take a direct callback. It's hard to write good robust code when mixing like that. So, since you are already using with promises in a number of operations, it's best to take the remaining async operations and "promisify" them so you can use them with promises. Then, you can take advantage of all the clean error propagation and chaining of promises.
Then, secondly, you have to make sure all your asynchronous operations in a loop are properly chained to the parent promise. To do that here, we will accumulate the promises from getAccount() in the loop into an array of promises. Then, after the loop, we can tell when they're all done using Promise.all() and then return that promise from readdir() so that they will all be properly chained to the parent promise.
This will then make sure everything inside the loop finishes before the parent resolves and before you call api.disconnect().
Here's what that would look like:
'use strict';
const RippleAPI = require('ripple-lib').RippleAPI;
const testFolder = '/home/ripple/.keystore';
const fs = require('fs');
const api = new RippleAPI({server: 'wss://s1.ripple.com'});
function getAccount(address) {
var balance = 0.0;
console.log("getting info for :"+address);
return api.getAccountInfo(address).then(info => {
var balance = info.xrpBalance;
console.log("balance of "+address+" is "+balance);
return balance;
});
}
// promisify readdir
const readdir = util.promisify(fs.readdir);
api.connect().then(() => {
return readdir(testFolder).then(function(files) {
const promises = [];
files.forEach(function(file) {
console.log(file);
promises.push(getAccount(file).then(balance => {
console.log(balance);
// make balance be the resolved value for this promise
return balance;
}));
});
// make sure we are waiting for all these promises to be done
// by chaining them into the parent promise
return Promise.all(promises);
});
}).then(balances => {
// process balances array here
console.log(balances);
return api.disconnect();
}, err => {
// disconnect, even in the error case, but preserve the error
console.error(err);
return api.disconnect().then(() => {throw err;})
}).then(() => {
console.log("done and disconnected");
});
Summary of changes made:
Promisify fs.readdir() so we can use promise with it
Change how we call readdir() to use promises
Collect getAccount() promises inside of .forEach() into an array of promises
Add return Promise.all(promises) so that we wait for all those promises to be done and so that it is chained into the parent promise
Make balance be the resolved value of each promise in the .forEach() loop, even after logging its value
Make sure we're calling api.disconnect() even in the error cases
After logging error, preserve the error value as the reject reason