javascript - not able to execute the promise from array one by one - javascript

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.

Related

How do you making javascript blocking code? [duplicate]

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
)
)

How to avoid using two loops when using Promise.all() dynamically?

Whenever you see Promise.all() being used, it is usually used with two loops, how can I make it into only one without using async and mantaining execution order?
The question is not about the order, I know promise.all preserves order, the question is about how to avoid two loops when you just need the returned value
function timeout(x){
return new Promise( resolve => {
setTimeout( () => {
return resolve(x);
},x)
})
}
const promises = [];
const results = [];
//First loop, array creation
for (i = 0; i < 20; i++) {
const promise = timeout(i*100)
promises.push(promise);
}
Promise.all(promises).then( resolvedP => {
//Second loop, async result handling
resolvedP.forEach( (timeout,i) => {
results.push({
index : i,
timeout : timeout
})
})
console.log(results);
})
//End of code
Now, this can be solved with async but in my context I can't use it, for example :
//Only one loop
for (i = 0; i < 20; i++) {
const timeoutAwait = await timeout(i*100);
results.push({
index : i,
timeout : timeoutAwait
})
}
console.log(results)
//End of code
What I have tried is the following, but the promise doesn't return the resolve value without using .then() :
for (i = 0; i < 20; i++) {
const promise = timeout(i*100)
promises.push(promise);
results.push({index : i, timeout : promise});
}
Promise.all(promises).then( resolvedP => {
resolvedP.forEach( (timeout,i) => {
results.push({
index : i,
timeout : timeout
})
})
console.log(results);
//results[0].timeout is a Promise object instead of 0
})
//End of code
So, is there any way I can make my first code sample in only one loop? Please ignore the context, is only an example.
function timeout(x) {
return new Promise(resolve => {
setTimeout(() => {
return resolve(x);
}, x);
});
}
const promises = [];
const results = [];
//First loop, array creation
for (let i = 0; i < 20; i++) {
const promise = timeout(i * 100).then(x => results.push({
index: i,
timeout: x
}));
promises.push(promise);
}
Promise.all(promises).then(() => {
console.log(results);
});
If you want to preserve execution/results order assign results using i index instead of .push
const promise = timeout(i * 100).then(x => results[i] = {
index: i,
timeout: x
});
As by the Promise.all documentation, the order will be preserved. It says about the return value:
A pending Promise in all other cases. This returned promise is then
resolved/rejected asynchronously (as soon as the stack is empty) when
all the promises in the given iterable have resolved, or if any of the
promises reject. See the example about "Asynchronicity or
synchronicity of Promise.all" below. Returned values will be in order
of the Promises passed, regardless of completion order.
(Highlighted by me.)
I find it more easy to understand if you map over an array of values instead of looping:
const timeout = x=>Promise.resolve(x);
Promise.all(
[...new Array(3).keys()]//array [0,1,2]
.map(x=>timeout(x*100))//[timeout(0*100),timeout(1*100),...
).then(
result=>result.map(//results of the timeout, map to object
(timeout,index)=>({
index,
timeout
})
)
).then(
result=>console.log('result:',result)
)
It's usually not a good idea to asynchronously mutate a shared variable (the results array). Just have the promise resolve and create the result you need in the last .then, now your promise resolves to the value you want.

Javascript promise chaining with promise.all not working?

I have the following code. When i run it i get this error " Cannot read property 'then' of undefined" at the first line in my code when calling getQueryToShift. It seems like getQueryToShift is not working like I intended. What is the correct way to use Promise.all so that the original promise I am declaring on the first line waits for all the promises in the promises array declared inside the getQueryToShift function to resolve before executing what is inside the then block ?
promise = getQueryToShift(hourDiff, options, map, baseBuildStart, oldFrom).then(values => { // error is here
// wait for promise before handling data in map passed as parameter
});
function getQueryToShift(hourDiff, options, map, baseBuildStart, oldFrom) {
let promises = [];
datasourceSrv.get(options.targets[0].datasource).then(ds => {
for (let i = 0; i < daysDiff - 1; i++) {
options.range.from._d = dateToMoment(oldFrom, false).add(i, 'h').toDate();
options.range.to._d = dateToMoment(options.range.from._d, false).add(1, 'h').toDate();
ds.query(options).then(result => {
promises.push(createQueryPromise(map, baseBuildStart, result.data));
});
}
return Promise.all(promises);
});
}
function createQueryPromise(map, baseBuildStart, data) {
return new Promise((resolve) => {
data.forEach(datum => {
//parsing data and adding it to map passed in as parameter
})
resolve();
});
}
The question is incomplete because createQueryPromise() function makes no sense by itself. It does not show an asynchronous operation at all, thus without one there is no need for a promise. And, if there is an asynchronous operation inside your .forEach() look, then you will need to coordinate that async operation, but you don't show the code for that so we can't help you fix that and we need to understand what that function is actually doing before we can help with a full and correct implementation of getQueryToShift().
On top of that, here are a couple other things that need fixing:
You aren't returning your promise from getQueryToShift(). Change this:
datasourceSrv.get(options.targets[0].datasource).then(ds => {
to this:
return datasourceSrv.get(options.targets[0].datasource).then(ds => {
And, you are ignoring the promise returned by:
ds.query(options).then(...)
Thus, your code wouldn't wait for that operation to finish. This is probably the promise that you need to collect in the array and use Promise.all() with, not the one you are doing it with.
Your code does the Promise.all on an empty array - you need to return the promise:
function* getDays(daysDiff, oldFrom) {
for (let i = 0; i < daysDiff - 1; i++) {
var options = {};
options.range.from._d = dateToMoment(oldFrom, false).add(i, 'h').toDate();
options.range.to._d = dateToMoment(options.range.from._d, false).add(1, 'h').toDate();
yield options;
}
}
function getQueryToShift(hourDiff, options, map, baseBuildStart, oldFrom) {
// return here
return datasourceSrv.get(options.targets[0].datasource).then(ds => {
return Promise.all(
Array.from(getDays(daysDiff, oldFrom), option => db.query(option))
);
});
}
Although I warmly recommend using an async function instead:
async function getQueryToShift(hourDiff, options, map, baseBuildStart, oldFrom) {
const ds = await datasourceSrv.get(options.targets[0].datasource);
await Promise.all(Array.from(getDays(daysDiff, oldFrom), db.query));
}

Wait promise inside for loop

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
)
)

How to create a loop of promises

so i have a promise that collects data from a server but only collects 50 responses at a time. i have 250 responses to collect.
i could just concate promises together like below
new Promise((resolve, reject) => {
resolve(getResults.get())
})
.then((results) => {
totalResults.concat(results)
return getResults.get()
})
.then((results) => {
totalResults.concat(results)
return getResults.get()
}).then((results) => {
totalResults.concat(results)
return getResults.get()
})
In this instance i only need 250 results so this seems a managable solution but is there a way of concating promises in a loop. so i run a loop 5 times and each time run the next promise.
Sorry i am new to promises and if this were callbacks this is what i would do.
If you want to loop and serialise the promises, not executing any other get calls once one fails, then try this loop:
async function getAllResults() { // returns a promise for 250 results
let totalResults = [];
try {
for (let i = 0; i < 5; i++) {
totalResults.push(...await getResults.get());
}
} catch(e) {};
return totalResults;
}
This uses the EcmaScript2017 async and await syntax. When not available, chain the promises with then:
function getAllResults() {
let totalResults = [];
let prom = Promise.resolve([]);
for (let i = 0; i < 5; i++) {
prom = prom.then(results => {
totalResults = totalResults.concat(results);
return getResults.get();
});
}
return prom.then(results => totalResults.concat(results));
}
Note that you should avoid the promise construction anti-pattern. It is not necessary to use new Promise here.
Also consider adding a .catch() call on the promise returned by the above function, to deal with error conditions.
Finally, be aware that concat does not modify the array you call it on. It returns the concatenated array, so you need to assign that return value. In your code you don't assign the return value, so the call has no effect.
See also JavaScript ES6 promise for loop.
Probably you just need Promise.all method.
For every request you should create a promise and put it in an array, then you wrap everything in all method and you're done.
Example (assuming that getResults.get returns a promise):
let promiseChain = [];
for(let i = 0; i <5; i++){
promiseChain.push(getResults.get());
}
Promise.all(promiseChain)
.then(callback)
You can read more about this method here:
Promise.all at MDN
EDIT
You can access data returned by the promises this way:
function callback(data){
doSomething(data[0]) //data from the first promise in the chain
...
doEventuallySomethingElse(data[4]) //data from the last promise
}

Categories