I have a function where I form the current sum of elements. Getcoin is the method in my Api, and it works correctly if I console.log each element during the foreach cycle. But as the result I got nothing in the return and console.log function
getCurrentCost() {
let currentCostArr = [];
let sum = 0;
let existingEntries = JSON.parse(localStorage.getItem("walletData"));
if (existingEntries == null) existingEntries = [];
existingEntries.forEach(el => {
this.coincapService
.getCoin(el.id)
.then((element) => {
currentCostArr.push(element.priceUsd * el.amount)
})
});
for (let i = 0; i < currentCostArr.length; i++) {
sum += +currentCostArr[i];
}
console.log('cevf c ' + sum)
console.log('current cost ' + currentCostArr)
return currentCostArr;
}
getCoin method
getCoin = async (id) => {
const coin = await this.getResource(`/assets/${id}`);
return this._transformCoin(coin);
}
The problem is that you are not handling correctly the Promise that .getCoin() returns inside the .forEach() 'loop'. It's making multiple calls to getCoin but the code outside the forEach is not awaiting these multiple async calls, so the next lines will execute without awaiting the result of the Promises. This is causing confusion in execution order logic of your code, behaving different than what you are expecting. Actually the function getCurrentCost() is ending before the Promises inside the loop resolves.
One way to solve your problem is replacing the .forEach() with .map(), which will return several Promises that will be awaited will Promise.all(), behaving the way it's expected.
//don't forget to turn the function async, so you can use await keyword.
async getCurrentCost() {
let sum = 0;
let existingEntries = JSON.parse(localStorage.getItem("walletData"));
if (existingEntries == null) existingEntries = [];
await Promise.all(existingEntries.map(async (el) => {
const coin = await this.coincapService.getCoin(el.id)
currentCostArr.push(coin.priceUsd * coin.amount)
})
return currentCostArr
}
Your forEach loop pushes values in you array asynchronously.
You calculate the sum before you get your array filled up with values.
Related
I did a function to find string enumerations but i didn `t succeed to repeat it in a async way, i'm not getting any result:
const stringEnumerationsAsync = async (str) => {
let strChars = str.split("");
let enumerations = new Set();
const getEnumerations = (chars, enumertion) => {
return new Promise((resolve) => {
if (chars.length === 0) {
resolve(enumertion);
} else {
for (let i = 0; i < chars.length; i++) {
let newChars = [...chars];
let char = newChars.splice(i, 1);
let newEnumeration = enumertion + char;
getEnumerations(newChars, newEnumeration).then((finalEnumeration) => {
enumerations.add(finalEnumeration);
});
}
}
});
};
await getEnumerations(strChars, "");
return [...enumerations];
};
There are a few issues:
with getEnumerations(newChars, newEnumeration).then((finalEnumeration)... you seem to expect one enumeration from the recursive call, but you should expect one or more enumerations.
In fact, you are mixing up two patterns: one where you collect the result from (resolved) return values, and one where you collect them in a non-local variable. So if you go for the latter, you should not expect anything useful from the resolved value you get from the recursive call. Instead add enumerations to the non-local variable in the base case, and only there.
In the else block you don't call resolve, meaning that the new Promise never gets resolved.
You should actually not need to create a new Promise in the else case, as you will get promises from the recursive calls, and it is an anti-pattern to wrap that inside a promise constructor call back. So the if...else should be moved outside of new Promise
And then in the if case you would get an immediately resolving promise, because you call resolve immediately. In that case you might as well call Promise.resolve instead of new Promise.
Even more simple is to make getEnumerations an async function so that you only need to return the resolution value, and since we don't care about the resolution value,... well, we just need to return ;-)
So that brings us to this code:
const stringEnumerationsAsync = async (str) => {
let strChars = str.split("");
let enumerations = new Set();
const getEnumerations = async (chars, enumertion) => {
if (chars.length === 0) {
enumerations.add(enumertion);
} else {
for (let i = 0; i < chars.length; i++) {
let newChars = [...chars];
let char = newChars.splice(i, 1);
let newEnumeration = enumertion + char;
await getEnumerations(newChars, newEnumeration);
}
}
};
await getEnumerations(strChars, "");
return [...enumerations];
};
// demo
stringEnumerationsAsync("abc").then(console.log);
console.log("this is synchronous");
let currentProduct;
for (let i = 0; i < products.length; i++) {
currentProduct = products[i];
subscription.getAll(products[i]._id)
.then((subs) => {
update(subs, currentProduct);
});
}
I'm using bluebird, the methods getAll and update return promises. How can I say "Wait until the two promises return, then update the currentProduct value"? I'm quite new to JS...
This will be straightforward if you can use async/await:
// Make sure that this code is inside a function declared using
// the `async` keyword.
let currentProduct;
for (let i = 0; i < products.length; i++) {
currentProduct = products[i];
// By using await, the code will halt here until
// the promise resolves, then it will go to the
// next iteration...
await subscription.getAll(products[i]._id)
.then((subs) => {
// Make sure to return your promise here...
return update(subs, currentProduct);
});
// You could also avoid the .then by using two awaits:
/*
const subs = await subscription.getAll(products[i]._id);
await update(subs, currentProduct);
*/
}
Or if you can only use plain promises, you can loop through all your products, and put each promise in the .then of the last loop. In that way, it will only advance to the next when the previous has resolved (even though it will have iterated the whole loop first):
let currentProduct;
let promiseChain = Promise.resolve();
for (let i = 0; i < products.length; i++) {
currentProduct = products[i];
// Note that there is a scoping issue here, since
// none of the .then code runs till the loop completes,
// you need to pass the current value of `currentProduct`
// into the chain manually, to avoid having its value
// changed before the .then code accesses it.
const makeNextPromise = (currentProduct) => () => {
// Make sure to return your promise here.
return subscription.getAll(products[i]._id)
.then((subs) => {
// Make sure to return your promise here.
return update(subs, currentProduct);
});
}
// Note that we pass the value of `currentProduct` into the
// function to avoid it changing as the loop iterates.
promiseChain = promiseChain.then(makeNextPromise(currentProduct))
}
In the second snippet, the loop just sets up the entire chain, but doesn't execute the code inside the .then immediately. Your getAll functions won't run until each prior one has resolved in turn (which is what you want).
Here is how I'd do it:
for (let product of products) {
let subs = await subscription.getAll(product._id);
await update(subs, product);
}
No need to manually chain promises or iterate arrays by index :)
You may want to keep track of what products you've processed because when one fails you have no idea how many succeeded and you don't know what to correct (if roll back) or retry.
The async "loop" could be a recursive function:
const updateProducts = /* add async */async (products,processed=[]) => {
try{
if(products.length===0){
return processed;
}
const subs = await subscription.getAll(products[0]._id)
await update(subs, product);
processed.push(product[0]._id);
}catch(err){
throw [err,processed];
}
return await updateProducts(products.slice(1),processed);
}
Without async you can use recursion or reduce:
//using reduce
const updateProducts = (products) => {
//keep track of processed id's
const processed = [];
return products.reduce(
(acc,product)=>
acc
.then(_=>subscription.getAll(product._id))
.then(subs=>update(subs, product))
//add product id to processed product ids
.then(_=>processed.push(product._id)),
Promise.resolve()
)
//resolve with processed product id's
.then(_=>processed)
//when rejecting include the processed items
.catch(err=>Promise.reject([err,processed]));
}
//using recursion
const updateProducts = (products,processed=[]) =>
(products.length!==0)
? subscription.getAll(products[0]._id)
.then(subs=>update(subs, product))
//add product id to processed
.then(_=>processed.push(products[0]._id))
//reject with error and id's of processed products
.catch(err=>Promise.reject([err,processed]))
.then(_=>updateProducts(products.slice(1),processed))
: processed//resolve with array of processed product ids
Here is how you'd call updateProducts:
updateProducts(products)
.then(processed=>console.log("Following products are updated.",processed))
.catch(([err,processed])=>
console.error(
"something went wrong:",err,
"following were processed until something went wrong:",
processed
)
)
I'm developing a Dapp based on Ethereum and I got stuck with Promises.
In the for loop, the elements of the array have to be verified one by one. This happens at the validateRow() function, which returns a Promise at first. The Promise will be resolved to a number (0, when the element is valid; 1, 2 or 3, when it is not valid).
In the end, I would like to return a resultList[], which is an array of objects. Each object should have two properties:
row, containing the element (a string),
and result, which tells whether it is valid.
However, the resultList[] only contains the rows in the end, while the 'then' branch only holds the results ({"row":"","result":"0"}). I added the logs which are printed in the console as comments. Unfortunately, I can't figure out, how I could put the two together.
var resultList = [];
for (var i = 0; i < App.resultArray.length; i++) {
var promiseReturned = contractInstance.validateRow.call(App.resultId, App.resultArray[i]);
console.log(promiseReturned); //PromiseĀ {<pending>}
var rowObject = new Object();
console.log(App.resultArray[i]); //row1string
rowObject.row = App.resultArray[i];
promiseReturned.then(function(returnVal) {
console.log("the returnVal: " + returnVal); //the returnVal: 1
rowObject.result = returnVal;
console.log("the rowObject :" + JSON.stringify(rowObject)); //{"row":"","result":"0"}
return returnVal;
});
resultList.push(rowObject);
};
console.log(resultList); //[{"row":"row1string"},{"row": "row2string"}]
return resultList;
In Javascript, use forward slashes to denote comments, not backslashes, else you'll get syntax errors.
Use Promise.all to wait for all promises to be resolved before returning the object:
async function getResultList() {
const allPromises = App.resultArray.map((row) => (
contractInstance.validateRow.call(App.resultId, row)
.then(result => ({ result, row }))
));
const resultList = await Promise.all(allPromises);
return resultList; // This will return a Promise to the caller of getResultList
}
Note that you'll have to consume getResultList as a promise, since it doesn't run synchronously. eg
const resultList = await getResultList();
For completeness, CertainPerformance's answer but using async/await, and rewritten more concisely:
async function getResultList() {
return await Promise.all(
App.resultArray.map(async (row) => {
const result = await contractInstance.validateRow.call(App.resultId, row);
return {
row,
result,
};
})
);
}
let currentProduct;
for (let i = 0; i < products.length; i++) {
currentProduct = products[i];
subscription.getAll(products[i]._id)
.then((subs) => {
update(subs, currentProduct);
});
}
I'm using bluebird, the methods getAll and update return promises. How can I say "Wait until the two promises return, then update the currentProduct value"? I'm quite new to JS...
This will be straightforward if you can use async/await:
// Make sure that this code is inside a function declared using
// the `async` keyword.
let currentProduct;
for (let i = 0; i < products.length; i++) {
currentProduct = products[i];
// By using await, the code will halt here until
// the promise resolves, then it will go to the
// next iteration...
await subscription.getAll(products[i]._id)
.then((subs) => {
// Make sure to return your promise here...
return update(subs, currentProduct);
});
// You could also avoid the .then by using two awaits:
/*
const subs = await subscription.getAll(products[i]._id);
await update(subs, currentProduct);
*/
}
Or if you can only use plain promises, you can loop through all your products, and put each promise in the .then of the last loop. In that way, it will only advance to the next when the previous has resolved (even though it will have iterated the whole loop first):
let currentProduct;
let promiseChain = Promise.resolve();
for (let i = 0; i < products.length; i++) {
currentProduct = products[i];
// Note that there is a scoping issue here, since
// none of the .then code runs till the loop completes,
// you need to pass the current value of `currentProduct`
// into the chain manually, to avoid having its value
// changed before the .then code accesses it.
const makeNextPromise = (currentProduct) => () => {
// Make sure to return your promise here.
return subscription.getAll(products[i]._id)
.then((subs) => {
// Make sure to return your promise here.
return update(subs, currentProduct);
});
}
// Note that we pass the value of `currentProduct` into the
// function to avoid it changing as the loop iterates.
promiseChain = promiseChain.then(makeNextPromise(currentProduct))
}
In the second snippet, the loop just sets up the entire chain, but doesn't execute the code inside the .then immediately. Your getAll functions won't run until each prior one has resolved in turn (which is what you want).
Here is how I'd do it:
for (let product of products) {
let subs = await subscription.getAll(product._id);
await update(subs, product);
}
No need to manually chain promises or iterate arrays by index :)
You may want to keep track of what products you've processed because when one fails you have no idea how many succeeded and you don't know what to correct (if roll back) or retry.
The async "loop" could be a recursive function:
const updateProducts = /* add async */async (products,processed=[]) => {
try{
if(products.length===0){
return processed;
}
const subs = await subscription.getAll(products[0]._id)
await update(subs, product);
processed.push(product[0]._id);
}catch(err){
throw [err,processed];
}
return await updateProducts(products.slice(1),processed);
}
Without async you can use recursion or reduce:
//using reduce
const updateProducts = (products) => {
//keep track of processed id's
const processed = [];
return products.reduce(
(acc,product)=>
acc
.then(_=>subscription.getAll(product._id))
.then(subs=>update(subs, product))
//add product id to processed product ids
.then(_=>processed.push(product._id)),
Promise.resolve()
)
//resolve with processed product id's
.then(_=>processed)
//when rejecting include the processed items
.catch(err=>Promise.reject([err,processed]));
}
//using recursion
const updateProducts = (products,processed=[]) =>
(products.length!==0)
? subscription.getAll(products[0]._id)
.then(subs=>update(subs, product))
//add product id to processed
.then(_=>processed.push(products[0]._id))
//reject with error and id's of processed products
.catch(err=>Promise.reject([err,processed]))
.then(_=>updateProducts(products.slice(1),processed))
: processed//resolve with array of processed product ids
Here is how you'd call updateProducts:
updateProducts(products)
.then(processed=>console.log("Following products are updated.",processed))
.catch(([err,processed])=>
console.error(
"something went wrong:",err,
"following were processed until something went wrong:",
processed
)
)
i want to perform synchronous operation of functions using promise. I have loop that passes the data to be inserted to insert function and after inserting one row i want to check the no. of rows exists in table hence i am performing select operation.
But the issue is if there are 3 records then it inserts all 3 records and after that my select function gets executed. what i want is after insertion of one record select function gets called.
here is my pseudo code as entire code involves lot of operations
for(var i=0; data.length ; i++){
self.executeFeedbackTrack(data);
}
executeFeedbackTrack:function(callInfo){
var self=this;
return self.insertFeedbackTrack(callInfo).then(function(data){
console.log("insertFeedbackTrack status "+status);
return self.getFeedbackTrack();
});
},
getFeedbackTrack :function(){
return new Promise(function(resolve,reject){
var objDBFeedbackTrack = new DBFeedbackTrack();
objDBFeedbackTrack.selectFeedbackTrack(function(arrayCallRegisters){
if(arrayCallRegisters){
console.log("notification.js no. of feedbacks "+arrayCallRegisters.length);
resolve(arrayCallRegisters.length);
}
});
});
},
insertFeedbackTrack :function(callInfo){
return new Promise(function(resolve,reject){
var objDBFeedbackTrack = new DBFeedbackTrack();
objDBFeedbackTrack.insertFeedbackTrack(callInfo.callNumber,callInfo.callServiceType,function(status){
resolve(status);
$('#loader').hide();
});
});
}
The previous answer is good, but if you are using nodejs, or babel, or you are using only modern browsers. You can use an async-await pair, it is es8 stuff.
let insertFeedbackTrack = function(){ return new Promise(/***/)};
let getFeedbackTrack = function(){ return new Promise(/***/)};
let processResult = async function(data){
let feedbacks = [];
for(let i=0;i<data.length;i++){
let insertedResult = await insertFeedbackTrack(data[i]);//perhaps you will return an id;
let feedbackTrack = await getFeedbackTrack(insertedResult.id);
feedbacks.push(feedbackTrack);
}
return feedbacks;
}
processResult(data).then(/** do stuff */)
It looks to me like this is caused by executing a series of asynchronous inserts, and assuming that the get of insert n (inside of a .then()) is called before insert n+1 is executed. However, I'm not aware of any such guarantee, in JavaScript; all that I'm familiar with is that then n will be called after insert n, not that it would be called before insert n+1.
What I'd suggest is avoiding this mix of traditional and callback-based code, and instead put the iteration step inside getFeedbackTrack().then. Assuming this understanding of the issue is correct, then something like the following should work:
function iterate(i) {
if (i < data.length) {
obj.insertFeedbackTrack(data[i]).then(function(insertResult) {
self.getFeedbackTrack().then(function(getResult) {
// this line is the important one, replacing the `for` loop earlier
iterate(i+1);
});
});
}
}
iterate(0);
By doing that, you would guarantee that insert for the next element does not occur until the current select executes successfully.
Naturally, you may also want to restructure that to use chained .then instead of nested; I used nested rather than chained to emphasize the ordering of callbacks.
This can be solved by using a very handy JS library Ramda. Concept is to use two methods, one is R.partial and another is R.pipeP.
First create a promises array from your data array, like following.
var promises = data.map(function(i) {
return R.partial(sample, [i])
});
Then you can pass this promise to R.pipeP, so that it can be executed one after another. like below.
var doOperation = R.pipeP.apply(this, promises)
Please execute following snippet attached.
// Sample promise returning function
function sample(d) {
return new Promise(function(resolve, reject){
setTimeout(function() {
console.log('resolved for:' + d);
resolve(d);
}, 1000)
})
}
// Sample data
var data = [1, 2, 3, 4, 5]
// Converting data array to promise array
var promises = data.map(function(i) {
return R.partial(sample, [i])
});
var doOperation = R.pipeP.apply(this, promises)
doOperation();
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>
So in your case, the code will look like this
var promises = data.map(function(i) {
return R.partial(self.executeFeedbackTrack, [i])
});
var doOperation = R.pipeP.apply(this, promises)
doOperation();
I use yield for such cases if using generator functions.
for(var i = 0; i < order.tasks.length; i++){
if(order.tasks[i].customer_id === 0){
var name = order.tasks[i].customer_name.split(" ")
const customers = yield db.queryAsync(
`INSERT INTO customers(
business_id)
VALUES(?)
`,[order.business_id])
}
}
Or else I use self-calling functions in case of callbacks.
var i = 0;
(function loop() {
if (i < data.length) {
task_ids1.push([order.tasks[i].task_id])
i++;
loop();
}
}());
Here's how I would sequentially call promises in a loop (I'm using ES7).
First, let's define some basic data:
const data = [0,1,2,3];
Then, let's simulate some long running process, so let's create a function that returns a Promise (you can think of this as a simulated network request, or whatever suits your needs)
const promiseExample = (item) =>
new Promise((res) => {
setTimeout(() => {
console.log('resolved ', item);
res(item);
}, 1000);
});
Now, let's create an array of promises. What the next line of code does is: for every item in the array data, return a promise factory. A promise factory is a function that wraps a certain promise without running it.
const funcs = data.map(item => async () => await promiseExample(item));
Now, the actual code starts here. We need a function that does the actual serialization. Since it has to handle an array of promiseFactories, I split it in two functions, one for the serialization of a single promise, and one for handling an array of promiseFactories.
const serializePromise = promiseFactoryList =>
promiseFactoryList.reduce(serialize, Promise.resolve([]));
const serialize = async (promise, promiseFactory) => {
const promiseResult = await promise;
const res = await promiseFactory();
return [...promiseResult, res];
};
Now, you can simply call it like this:
serializePromise(funcs).then(res => {
console.log('res', res);
});
As you can see, the code is pretty simple, elegant, functional, and doesn't need any external dependency. I hope this answers your question and helps you!
const serializePromise = promiseFactoryList =>
promiseFactoryList.reduce(serialize, Promise.resolve([]));
const serialize = async (promise, promiseFactory) => {
const promiseResult = await promise;
const res = await promiseFactory();
return [...promiseResult, res];
};
const data = [0,1,2,3];
const promiseExample = (item) =>
new Promise((res) => {
setTimeout(() => {
console.log('resolved ', item);
res(item);
}, 1000);
});
const funcs = data.map(item => async () => await promiseExample(item))
serializePromise(funcs).then(res => {
console.log('res', res);
});
I ran into this problem recently and solved it as shown below. This is very similar to the answer by #Ethan Kaminsky, but only uses callbacks. This may be useful for people avoiding promises for whatever reason.
In my application the asynchronous function may fail and can safely be retried; I included this logic because it's useful and doesn't overly complicate the routine, but it is not exercised in the example.
// Some callback when the task is complete
function cb(...rest) { window.alert( `${rest.join(', ')}` ) }
// The data and the function operating on the data
// The function calls "callback(err)" on completion
const data = [ 'dataset1', 'dataset2', 'dataset3' ]
const doTheThing = (thingDone) => setTimeout( thingDone, 1000 )
let i = -1 // counter/interator for data[]
let retries = 20 // everything fails; total retry #
// The do-async-synchronously (with max retries) loop
function next( err ) {
if( err ) {
if( ! --retries ) return cb( 'too many retries' )
} else if( ! data[++i] ) return cb( undefined, 'done-data' )
console.log( 'i is', i, data[i] )
doTheThing( next, data[i] ) // callback is first here
}
// start the process
next()