Chained promises in Promise.all without wrapper Promise - javascript

Is it possible for Promise.all to return the last value of the chain without a wrapper promise?
Without using await, it doesn't work in my context
Without wrapper example :
function sum1(x){
return new Promise(resolve => {
setTimeout(t => resolve(x+1),3000)
})
}
const p1 = sum1(1);
p1
.then(sum1)
.then(sum1)
Promise.all([p1])
.then(v => console.log(v[0]));
It logs 2 instead of the expected 4.
But if I use a wrapper it works :
function sum1(x){
return new Promise(resolve => {
setTimeout(t => resolve(x+1),3000)
})
}
function sum3(x){
return sum1(x)
.then(sum1)
.then(sum1)
}
const p2 = sum3(1);
Promise.all([p2])
.then(v => console.log(v[0]));
But in my context it gets complicated if I need to create and name a wrapper function for every chain of promises...
Is this possible?

You can store the value returned by p1.then(sum1).then(sum1) and call Promise.all on this value. It waits for the resolution of the promise chain not only the first one. Here is an example:
function sum1(x) {
return new Promise(resolve => {
setTimeout(t => resolve(x + 1), 10);
});
}
const p1 = sum1(1);
const p2 = p1.then(sum1).then(sum1);
Promise.all([p1]).then(v => console.log('P1', v[0]));
Promise.all([p2]).then(v => console.log('P2', v[0]));

Actually all I had to do was call the chain in the variable declaration, so it references the last called promise
function sum1(x){
return new Promise(resolve => {
setTimeout(t => resolve(x+1),3000)
})
}
//The change is here
const p1 = sum1(1)
.then(sum1)
.then(sum1)
Promise.all([p1])
.then(v => console.log(v[0]));

Explanation: the problem with your code was that you store in const p1 = sum1(1); only first part of chain, and in Promise.all([p1]) you get result only from this first part (one solution is just to store all chain in p1 like this: p1=sum1(1).then(sum1).then(sum1). However in your case, you don't need to use Promie.all at all (because in your example there is only one promise p1/2 ):
function sum1(x){
return new Promise(resolve => {
setTimeout(t => resolve(x+1),300)
})
}
// Promise.all([sum1(1).then(sum1).then(sum1)]).then(r => console.log(r)); // this works too
sum1(1).then(sum1).then(sum1).then(r => console.log(r));

How about create a new function to do your task, it seems like promise.all does not fit to your case
const runInWaterfall = (promises) => new Promise((resolve, reject) => {
const result = promises.reduce((acc, curr, index) => {
if(!acc) {
return curr();
}
return acc.then(curr);
}, null);
result.then(resolve).catch(reject);
})
and your task can be rewritten as following
runInWaterfall([() => Promise.resolve(1), sum1, sum1, sum1]).then(result => console.log(result))

Related

Taking a value from Promise and returning it instead of console.log

I have an exercise where I have to build some async functions (not using await/async, Promises are allowed).
funcs need to be a list of those x functions I make.
Each of the function has to receive callback as an argument.
The callback is made with (err,res) where err could be either null or the statement of error (I don't know how to determine any error there).
Function together need to return an Array with the results of those functions, for which the pred is fulfilled.
Before I go to together function, I have to ask you, how to get into the value of Promise if I don't want to console.log it, but I want for example to either return it in callback or just put it into a const. If I make res.then(...) I am only able to console.log that result, but am not able to put it into let result (because of async probably the result is always returned as 0).
My code:
function fun1(value,cb) {
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => resolve( value * 100), 500)
})
return cb(null,promise1)
}
function fun2(value,cb) {
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => resolve( value * 100), 500)
})
return cb(null, promise2)
}
function fun3(value,cb) {
const promise3 = new Promise((resolve, reject) => {
setTimeout(() => resolve( value * 100), 2000)
})
return cb(null, promise3)
}
function callback(err, res) {
let result = 0;
res.then(x => {
if(x>200) {
console.log(x)
result = x
}
else {
console.log(err)
result = err
}
})
}
console.log(fun1(1,callback))
//const together = (funcs, pred) => (n) => { ... };

Order of promise wrong

I would like to return a list of promises with Promise.all, but the result is always empty.
Here is my theoric code:
function myFunction() {
const pSqlLines = sql.openDatabase().then(mdb => {
const query = `SELECT data FROM database`
return mdb.prepare(query).all()
})
const globalPromise = new Promise( resolve => {
const promises = []
pSqlLines.then( sqlLines => {
sqlLines.forEach(line => {
promiseA()
.then(res1 => { if (res1 == 1) return promiseB() })
.then(res2 => { if (res2 == 1) return promiseC() })
.then(res3 => { if (res3 == 1) promises.push( new Promise(resolve => resolve(line) ) ) })
}) // The promises chaining is correct as the 'promises' array is correctly fulfilled, but later
resolve( Promise.all(promises) ); // It is always called before the foreach loop. Why?
})
})
return globalPromise.then( result => console.log(result) )
}
Someone can help me please?
Thanks a lot.
You should not need to create a new promise with new Promise when you already have a promise to work with -- in your case: pSqlLines. Wrapping pSqlLines inside a new Promise wrapper is an example of the promise constructor antipattern.
Secondly, this code:
new Promise(resolve => resolve(line) )
...can be replaced with:
Promise.resolve(line)
...and since it is to serve as the return value for a then callback, it can be just:
line
As to your question: you are indeed calling Promise.all(promises) at a moment that the promises array is still empty, as the forEach loop has only executed Promise() and its then chain, but none of the asynchronous callbacks that are passed to those then calls have executed yet. You didn't await the resolution of these promises.
I get from your code that you want to exclude some line values depending on conditions. In that case the promise will resolve to undefined. I suppose you would want to exclude those undefined values, and so maybe a filter(Boolean) is appropriate.
Here is a theoretical solution for your theoretical code:
function myFunction() {
const pSqlLines = sql.openDatabase().then(mdb => {
const query = `SELECT data FROM database`;
return mdb.prepare(query).all();
});
return pSqlLines.then( sqlLines => {
const promises = sqlLines.map(line => {
return promiseA()
.then(res1 => { if (res1 == 1) return promiseB(); })
.then(res2 => { if (res2 == 1) return promiseC(); })
.then(res3 => { if (res3 == 1) return line; });
}));
return Promise.all(promises);
}).then(result => result.filter(Boolean));
}

Access promise instance that returns from async function

Async function automatically returns promise - I wonder if there is a way somehow to get this instance of this promise inside the function
For example if I return an actual promise like this:
const getSomePromise = () => {
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success');
}, 1000);
})
promise.someProp = 'myProp';
return promise;
}
const promise = getSomePromise();
console.log(promise.someProp);
I want to achieve the same thing with pure async function:
const sleep = ts => new Promise(resolve => setTimeout(resolve, ts));
const getSomePromise = async () => {
const p = await sleep(1000);
// some how access the instance of the promise from within the async function
// for example this['someProp'] = 'myProp';
// and return the all promise with this prop
return 'sucess';
}
const promise = getSomePromise();
console.log(promise.someProp);
Can I do that ?
Thanks
Adding a property to the promise is almost certainly a bad idea (more on that later, under However), but just to talk about how you would continue to do it:
I wonder if there is a way somehow to get this instance of this promise inside the function
No, there isn't. You could create a promise within the function and return it, but that wouldn't be the promise the function returns (it would just affect how the promise the function returns resolves).
If you want to add a property to the promise being returned, you'll have to use a non-async function. You might make the function's entire code non-async:
const sleep = ts => new Promise(resolve => setTimeout(resolve, ts));
const getSomePromise = () => {
const p = sleep(1000).then(() => 'success');
p.someProp = 'myProp';
return p;
}
const promise = getSomePromise();
console.log(promise.someProp);
...or you might use an inner async function so you can use await semantics and such:
const sleep = ts => new Promise(resolve => setTimeout(resolve, ts));
const getSomePromise = () => {
const p = (async () => {
await sleep(1000);
return 'success';
})();
p.someProp = 'myProp';
return p;
}
const promise = getSomePromise();
console.log(promise.someProp);
However: Adding a property to the promise is almost certainly a bad idea. Instead, have the promise resolve to an object with properties both for the resolution and the extra someProp:
const sleep = ts => new Promise(resolve => setTimeout(resolve, ts));
const getSomePromise = async () => {
const p = await sleep(1000);
// some how access the instance of the promise from within the async function
// for example this['someProp'] = 'myProp';
// and return the all promise with this prop
return {
result: 'success',
someProp: 'myProp'
};
}
getSomePromise()
.then(resolution => {
console.log(resolution.someProp);
});

JavaScript Promises: conventional way to pass arguments

How do i pass additional arguments to next "step" of promise?
new Promise((resolve, reject) => {
const a = // do stuff and return a string
return Promise.all([
// execute another promise,
// execute yet another promise
])
})
.then(([resultFromPromise_1, resultFromPromise_2]) => {
// how do i pass `const a` here?
})
I can add something like new Promise(resolve => resolve(a)) into Promise.all array, but this looks ugly. Is there better way to pass data in such cases?
I can add something like new Promise(resolve => resolve(a)) into Promise.all array, but this looks ugly. Is there better way to pass data in such cases?
Yes: Use then. If you already have a promise, using new Promise is never needed. then creates a promise, which waits for the resolution of the one you called it on, and then gets resolved with what you return from the then callback or gets rejected if you throw an exception. One of the keys of promises is how using then (and catch) transforms things at each link in the chain.
In that specific case, you'd use then on the original promise and use its callback to transform the result using a (although if you want to wait until they're all done, you can do that too; covered later).
Side note: The new Promise line at the beginning of the code of your question shouldn't be there, you don't return a promise out of the promise executor (the callback you pass to new Promise).
Example:
const a = "some string";
Promise.all([
getPromise("one").then(result => result + " - " + a), // ***
getPromise("two")
])
.then(results => {
console.log(results);
});
function getPromise(str) {
// (Could use Promise.resolve here; emphasizing asynchronousness)
return new Promise(resolve => {
setTimeout(() => {
resolve(str);
}, 250);
});
}
Alternately, if you really only want to use a when all of the promises you're passing to Promise.all have resolved, you can do that, too:
const a = "some string";
Promise.all([
getPromise("one"),
getPromise("two")
])
.then(([result1, result2]) => {
return [result1 + " - " + a, result2]; // ***
})
.then(results => {
console.log(results);
});
function getPromise(str) {
// (Could use Promise.resolve here; emphasizing asynchronousness)
return new Promise(resolve => {
setTimeout(() => {
resolve(str);
}, 250);
});
}
First off, your first promise has an error, you're not resolving it. You should do something like this:
new Promise((resolve, reject) => {
const a = 1;
resolve(Promise.all([
...
]))
})
And as for your question, instead of new Promise(resolve => resolve(a)) you can just pass a directly to the all array. ie:
new Promise((resolve, reject) => {
const a = 1;
resolve(Promise.all([
Promise.resolve("a"),
Promise.resolve("b"),
a,
]))
})
.then(([resultFromPromise_1, resultFromPromise_2, a]) => {
console.log(a);
})

How to structure nested Promises

I have a situation where I think the only choice for me is to nest some Promises within each other. I have a Promise that needs to be performed and a method that does something until that Promise is complete. Something like this:
let promise = new Promise((resolve, reject) => {
// Do some stuff
});
doSomethingUntilPromiseisDone(promise);
However, within my Promise, I need to execute another method that returns another Promise:
let promise = new Promise((resolve, reject) => {
fetchValue(url)
.then((value) => {
// Do something here
}).catch((err) => {
console.error(err);
});
});
doSomethingUntilPromiseisDone(promise);
But now, in the fetchValue method's then statement, I have another method I need to execute that, guess what, returns another Promise:
let promise = new Promise((resolve, reject) => {
fetchValue(url)
.then((value) => {
saveToCache(value)
.then((success) => {
console.log('success!!');
resolve('success');
});
}).catch((err) => {
console.error(err);
});
});
doSomethingUntilPromiseisDone(promise);
So in the end, I have a Promise, within a Promise, within a Promise. Is there someway I can structure this better so that it is more straightforward? It seems like nesting them within each other is counter to Promise's intended chaining approach.
Use .then()
let doStuff = (resolve, reject) => {/* resolve() or reject() */};
let promise = new Promise(doStuff);
doSomethingUntilPromiseisDone(
promise
.then(value => fetchValue(url))
.then(value => value.blob())
.then(saveToCache)
)
.then(success => console.log("success!!"))
.catch(err => console.error(err))
you can use generator to flatten your nested promises (Bluebird.couroutine or Generators)
//Bluebird.couroutine
const generator = Promise.coroutine(function*() {
try {
const value = yield fetchValue(url);
const success = yield saveToCache(value);
console.log('success:', success);
} catch(e) {
console.error(err);
}
}));
generator();
Each function will call the next one with the result of the method before.
var promises = [1,2,3].map((guid)=>{
return (param)=> {
console.log("param", param);
var id = guid;
return new Promise(resolve => {
// resolve in a random amount of time
setTimeout(function () {
resolve(id);
}, (Math.random() * 1.5 | 0) * 1000);
});
}
}).reduce(function (acc, curr, index) {
return acc.then(function (res) {
return curr(res[index-1]).then(function (result) {
console.log("result", result);
res.push(result);
return res;
});
});
}, Promise.resolve([]));
promises.then(console.log);

Categories