Javascript Promise push value into array (only from function or outside?) - javascript

I have some promises and a Promise.all:
array = [];
var one = new Promise(function(resolve, reject) {
// Do stuff
setTimeout(function() {
resolve('One Done');
array.push('one');
}, 5000);
});
var two = new Promise(function(resolve, reject) {
// Do Stuff
resolve('Two Done');
array.push('two');
});
Promise.all(array).then(values => {
console.log(values);
});
We know this doesn't work because array.push needs to be outside.
I currently have a few functions which I need to have called by promises so that finally I can have it in Promise.all.
Would it be advisable to call the function from inside the promise like this:
function dosomething() {
// does something
array.push('something');
}
var mypromise = new Promise(function(resolve, reject) {
dosomething();
resolve('Did something');
});
Or is there a more advisable way to do this?

Promise.all waits for an array of Promises. In your example, you are always pushing string types into an array. This obviously won't work. In your first example, you want to push the promises themselves:
array = [];
var one = new Promise(function(resolve, reject) {
// Do stuff
setTimeout(function() {
resolve('One Done');
}, 5000);
});
array.push(one);
var two = new Promise(function(resolve, reject) {
// Do Stuff
resolve('Two Done');
});
array.push(two);
Promise.all(array).then(values => {
console.log(values);
});
As long as the array contains Promise objects, Promise.all will work as expected.

Promise.All expects an array of promises, and will wait until all promises are fullfilled, providing you with the results of each Promise or a catch if any of them fails.
You can resolve them with any type of object if you are willing to resolve it with more than just a string, and you can use the results afterwards in any way you want, so you could avoid messing with the array inside from the promise and instead resolve the "work item" as a unit and use all required results after the async evaluation.
This will also ( i think ) make the code cleaner and more managable.
You could instead try to do this:
var valuesArray=[];
var prom1 = new Promise(function(resolve, reject) {
// Do stuff
setTimeout(function() {
resolve({ msg: 'One Done', data : 'one'});
// array.push('one');
}, 5000);
});
var prom2 = new Promise(function(resolve, reject) {
// Do Stuff
resolve({ msg: 'Two Done', data : 'two'});
// array.push('two');
});
var promisesArray= [prom1,prom2];
Promise.all(promisesArray).then(values => {
// do stuff with values here
console.log(values[0].msg);
console.log(values[1].msg);
valuesArray.push(values[0].data);
valuesArray.push(values[0].data);
});

I think it would be clearest if you called Promise.all with the array of Promises, with each Promise resolving to the desired value, no outer array nor push at all:
var one = new Promise(function(resolve, reject) {
// Do stuff
setTimeout(function() {
console.log('One Done')
resolve('one');
}, 1000);
});
var two = new Promise(function(resolve, reject) {
// Do Stuff
console.log('Two Done');
resolve('two');
});
Promise.all([one, two]).then(arr => {
console.log(arr);
});
If you need both values (the 'One Done' and the 'one'), you can resolve the initial promises with an array with both values, doing whatever you need to with the 'One Done', and then resolving with the 'one' to be chained with array created by Promise.all:
const logAndReturn = ([logStr, finalResolveStr]) => {
console.log(logStr);
return finalResolveStr;
}
var one = new Promise(function(resolve, reject) {
// Do stuff
setTimeout(function() {
resolve(['One Done', 'one']);
}, 1000);
});
var two = new Promise(function(resolve, reject) {
// Do Stuff
resolve(['Two Done', 'two']);
});
Promise.all([
one.then(logAndReturn),
two.then(logAndReturn),
]).then(arr => {
console.log(arr);
});

Related

Promise.all cannot find Resolve for parent Promise

I have a page that contains N html selects. I have a function called 'run_all_ajax' that loops through them and calls an ajax function that populates them. I want a promise to return from run_all_ajax only when Promise.all completes within it.
Right now, the console reads;
all done, 0
after all done
0,1,2,3, etc
meaning that the promise.all is resolving before i have added to the promises array. How do I correct this?
Also, I want to understand how the array of promises works in terms of timing. Are we assuming that we can loop through the array of selects and add them to the array of promises faster than we could resolve all outstanding promises with the Promise.all()?
function run_all_ajax() {
return new Promise(function (resolve, reject) {
var promises = [];
$("[selectgrid]").each(function (i, obj) {
$.ajax({
//stuff
success: function (response) {
//stuff
console.log(i);
}, //end: success
complete: function (jqXHR, textStatus) {//
promises.push(new Promise(function (resolve, reject) { resolve("Complete"); }));
}
}); //end: $.ajax
});
Promise.all(promises).then(function (values) {
console.log('all done, ' + promises.length);
resolve("Complete");
});
}); //end promise
}
run_all_ajax().then(function(){
console.log('after all done');
})
You push a promise at the wrong place (time). There is no need to create a new Promise. The promise you need is returned by $.ajax().
So do:
promises.push($.ajax({
//stuff
}));
promises.push is called when a request is complete which is way after Promise.all is executed.
Basically you are calling Promise.all([]) which resolves immediately.
This
new Promise(function (resolve, reject) { resolve("Complete"); })
also resolves immediately. It's equal to Promise.resolve("Complete")

Concurrency in resolving an array of promises

So basically I found that promises would resolve concurrently when you create an array of promises and deal with individual promises in a loop... But it doesn't work when you create individual promises and deal with them in a loop...
So basically this question comes from me playing with axios and I originally thought this was a bug (https://github.com/axios/axios/issues/1973). However I soon discovered it was actually a problem with misunderstanding some part of Promise, especially await Promise. However I don't think I could find anything in the MDN document unless I have missed something.
This will not work:
let results = [];
for (let i=1; i<=5; i++) {
let promise = new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('foo');
}, 10000);
})
results.push(await promise);
}
But this will:
let results = [];
const promises = [
new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('foo');
}, 10000);
}),
new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('foo');
}, 10000);
}),
new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('foo');
}, 10000);
}),
new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('foo');
}, 10000);
}),
new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('foo');
}, 10000);
}),
];
for (let i=1; i<=5; i++) {
results.push(await promises[i-1]);
}
I would expect, in both cases, that the array of promises (results) to resolve in a concurrent manner.
However in the first example, the promises actually resolve one after another.
Okay... I was just being silly...
The first case, we are creating five individual promises through loops, of course we have to wait until one resolves before executing the second loop.
The second case, we are creating the array of promises first, so they start running concurrently. When we loop through 5 of them, we would actually be blocked at the first loop until the first promise resolves, the rest 4 loops would then finish at no time (because the rest 4 promises will be resolved).
This is just because 5 promises are running concurrently in the array.

One Promise for Multiple Promises - Concurrency Issue

In the method "myMethod" of my "gulpfile.js" I want to create multiple Promises. The quantity depends on the array size (method parameter). When I call the method I want to make sure that all promises are fulfilled before I continue. I would prefer to return not before all promises are fulfilled.
Please have a look at the last five lines of code.
Dependencies
var promiseAll = require('gulp-all');
var del = require('del');
var deleteEmpty = require('delete-empty');
gulp-all | del | delete-empty
Helper Method
var oneForAllPromises = function(promises){
var promAll = promiseAll(promises);
promAll.then(function(param) {
console.log('foo');
}, function(err) {
console.error('foo');
});
return promAll;
}
Problematic Code
var myMethod = function(array1, array2){
var promise = del(array1, {force: true});
promise.then(paths => {console.log('foo');});
var promises = [];
promise.then(()=>{
for(var i=0; i<array2.length; i++){
promises[i] = new Promise(function(resolve, reject) {
deleteEmpty(array2[i], {force: true},
function(err, deleted){
if(err){
console.log('foo');
reject
}else{
console.log('foo');
resolve
}
}
);
});
}
});
// PROBLEM: Returns empty promises array
console.log("promiesesLENGTH: "+promises.length); // promiesesLENGTH: 0
// Create one promise for all the promises
return oneForAllPromises(promises);
}
At the time of the console.log, the first promise promise = del(array1, {force: true}); is not yet finished, so none of the code in the then is yet executed. That's why your promises are empty.
You can simply return in a then another promise:
var myMethod = function(array1, array2){
var promise = del(array1, {force: true});
return promise.then(() => {
return Promise.all(array2.map(array2value => {
return new Promise(function(resolve, reject) {
deleteEmpty(array2value, {force: true}, (err, deleted) => {
if (err) {
reject(err);
} else{
resolve()
}
});
});
}
});
}

Understanding .then() ES6

I ran into a bug in my code that puzzled me for a long time and am looking for some clarification.
In this code, the commented out inner promise was causing a problem. The Promise.all() at the end was continuing as soon as the setTimeout hit, not after the resolve inside the timeout.
Wrapping the async code with a promise fixes the flow problem, but why is this?
Essentially, why can't we just run normal async code in a .then() chain, an return a Promise.resolve() at the end of the async callback?
var asyncPromise = function() {
return new Promise(function(resolve, reject) {
setTimeout(function() {
console.log('Async Promise done');
resolve();
}, 1000);
});
};
var generateSignupPromises = function(qty) {
var promiseArray = [];
for (var i = 1; i <= qty; i++) {
promiseArray.push(
function() {
return asyncPromise()
.then(function() {
console.log('Before Timeout');
//Uncommenting this fixes the issue
//return new Promise(function(resolve, reject) {
setTimeout(function() {
console.log('After Timeout');
//resolve();
return Promise.resolve();
}, 500);
//})
});
}
);
}
return promiseArray;
};
var test = generateSignupPromises(1);
Promise.all([test[0]()])
.then(function() {
console.log('Done');
});
Link to running code: http://www.es6fiddle.net/imfdtuxc/
why can't we just run normal async code in a .then() chain, an return a Promise.resolve() at the end of the async callback?
You perfectly can. But any value - be it a promise or whatever - being returned from a plain asynchronous callback is just ignored as usual.
There is nothing that starting the asynchronous action inside a then callback changes about this, setTimeout just doesn't return a promise - and the then won't know about anything asynchronous happening that it could wait for.
If you want to return a promise from a callback and get another promise for that eventual result, it has to be a then callback:
asyncPromise()
.then(function() {
return new Promise(function(resolve, reject) {
// ^^^^^^
setTimeout(resolve, 500);
}).then(function() {
// ^^^^^^^^^^^^^^^
return Promise.resolve();
});
});
Then is a sync function so if you want to do async task inside then, you has to return a Promise.
Also, Promise.all expect an array of promises. Not an array of an array.
var asyncPromise = function() {
return new Promise(function(resolve, reject) {
setTimeout(function() {
console.log('Async Promise done');
resolve();
}, 1000);
});
};
var generateSignupPromises = function(qty) {
var promiseArray = [];
for (var i = 1; i <= qty; i++) {
promiseArray.push(
function() {
return asyncPromise()
.then(function() {
console.log('Before Timeout');
//Uncommenting this fixes the issue
return new Promise(function(resolve, reject) {
setTimeout(function() {
console.log('After Timeout');
resolve();
//return Promise.resolve();
}, 500);
})
});
}
);
}
return promiseArray;
};
var test = generateSignupPromises(1);
Promise.all([test[0]()])
.then(function() {
console.log('Done');
});
http://www.es6fiddle.net/imfe2sze/

JS ES6 Promise Chaining

I'm trying to learn how to use promises, but am having trouble comprehending the chaining. I assume that with this code, both promises will run. Then when I call test.then() it should know that test has resolved and pass the resolve data to then().
Once that function finishes, it goes onto the next then(), repeating the same process with the test2 promise.
However, I can only get it to print out the first promise results, not the second. Any ideas what is missing here?
var test = new Promise(function(resolve, reject){
resolve('done1');
});
var test2 = new Promise(function(resolve, reject){
resolve('done2');
});
test
.then(function(data) {
console.log(data);
})
.then(test2)
.then(function(data) {
console.log(data);
});
Your first .then call is returning undefined, whereas any subsequent .then is expecting a returned promise. So you'd need to change your code to:
var test = new Promise(function(resolve, reject){
resolve('done1');
});
var test2 = new Promise(function(resolve, reject){
resolve('done2');
});
test
.then(function(data) {
console.log(data);
return test2;
})
.then(resultOfTest2 => doSomething)
.then(function(data) {
console.log(data);
});
You need to return next promise from the then callback:
test.then(function(data) {
console.log(data);
return test2;
}).then(function(data) {
console.log(data);
});
Summary:
The basic concept of promise chaining with promises is that every then / catch method on a fulfilled promise returns another promise. It works in the following manner:
When a promise is resolved the callback passed in the then method is called. The then method wraps the value which is returned in its callback in a resolved promise and returns this resolved promise.
When a promise is rejected the callback passed in the catch method is called. The catch method wraps the value which is returned in its callback in a rejected promise and returns this rejected promise.
Example:
Before fully understanding the concept of chaining multiple then methods it is important to know what exactly the return values of then and catch are. Take the following example:
let prom1 = new Promise((res, rej) => {
res('res');
});
const resolvedProm1 = prom1.then((val) => {return val});
// setTimeout needed for the promise to actually be resolved
setTimeout(() => console.log(resolvedProm1));
let prom2 = new Promise((res, rej) => {
rej('rej');
});
const resolvedProm2 = prom2.catch((err) => {throw err});
// setTimeout needed for the promise to actually be rejected
setTimeout(() => console.log(resolvedProm2));
We can observe the status of the promises in the chrome devtools:
What basically happens is that in a then or catch callback is the following:
Any value returned in a then or catch callback is wrapped in Promise.resolve() and a new resolved promise is returned.
Any error thrown in a then or catch callback is wrapped in Promise.reject() and a new rejected promise is returned.
Because we are getting returned a rejected or resolved promise object we can repeat the cycle and call the then or catch method on it again. For example:
const prom = new Promise((res, rej) => {
if (Math.random() > 0.5) {
res('success');
} else {
rej('error');
}
});
prom.then((val) => {
return val;
}).then((val) => {
return val
}).then((val) => {
console.log(val)
}).catch((err) => {
console.log('err');
})
This calling of then and catch methods which are executed in their respective order is called promise chaining. It is a very useful technique to make working with asynchronous code easier, especially if multiple asynchronous operations need to be performed which are dependend on each others data.
you need to return the other promise(test2) in the first promise (test1) to allow for chaining:
var test = new Promise(function(resolve, reject){
resolve('done1');
});
var test2 = new Promise(function(resolve, reject){
resolve('done2');
});
test
.then(function(data) {
console.log(data);
return test2;
});
You may also want to try -
let test = new Promise(function(resolve, reject){
resolve('done1');
});
let test2 = new Promise(function(resolve, reject){
resolve('done2');
});
try {
let logOne = test();
let logTwo = test2();
console.log(logOne);
console.log(logTwo);
} catch(error) {
console.error(error);
}
In this way, you can also properly handle any promise dependencies. For example if test one relied on test two's data your could -
try {
let logOne = test();
let logTwo = test2(logOne);
console.log(logOne);
console.log(logTwo);
} catch(error) {
console.error(error);
}

Categories