Promise in promise? - javascript

Is there any way I can make the following work:
function A
This function returns a promise that does a task which uses multiple webworkers, once the webworkers have collectively finished this task it will resolve the promise.
function B
This function executes function A, and after that it executes multiple other functions that require A to have been done first.
function C
This function executes function B, and after that it executes multiple other functions that require A. This function may onle be called once, as it is a single process which improves a certain genetic algorithm task by executing function B multiple times sequentially. (so no callback B -> C).
Something like this:
var someObject = {test: null};
function A () {
return new Promise((resolve, reject) => {
// do some task which requires webworkers
someObject.test = 10;
});
}
async function B(){
await A();
someObject.test *= 4;
return someObject.test;
}
function C(){
// set up complicated options here
// a loop using these options
var i = 100;
while(i--){
var x = B();
console.log(x);
// do tasks that require B;
}
}
C();
View it here on JSFiddle, please note that I want to avoid any kind of callbacks because I am already working with a complicated object architecture (i'm doing neuro-evolution).
The best thing I could think of is somehow wrapping function B in a promise as well, but I haven't figured out how to do that.

You can as well call await on async functions as they implicitly return a promise:
var someObject = {test: null};
function A () {
return new Promise((resolve, reject) => {
// do some task which requires webworkers
someObject.test = 10;
setTimeout(resolve, 1000);
});
}
async function B(){
await A();
someObject.test *= 4;
return someObject.test;
}
async function C(){
// set up complicated options here
// a loop using these options
var i = 100;
while(i--){
var x = await B();
console.log(x);
// do tasks that require B;
}
}
C();

You could return a promise from B(). Note, you don't need to resolve someObject in B() (i.e. resolve(someObject.test)) because it's a global var, but I did in order to demonstrate how to resolve a value and to follow your example code. Also note I removed the loop for example sake.
EDIT - thanks to #t.niese and others for pointing out the anti-pattern in my original answer. Please see the updated answer, below
https://jsfiddle.net/ey07zy6h/9/
var someObject = {test: null};
function A () {
console.log('in a')
return new Promise((resolve, reject) => {
// do some task which requires webworkers
someObject.test = 10;
resolve()
});
}
function B() {
console.log('in b')
return A()
.then(() => new Promise((resolve, reject) => {
someObject.test *= 4;
resolve(someObject.test);
}))
.catch(e => {
console.log('Err in A: ' + e)
})
}
function C() {
// set up complicated options here
console.log('in c')
// a loop using these options
var i = 100;
// while(i--) {
B()
.then(data => {
let x = data;
console.log('X is: ' + JSON.stringify(x));
// do tasks that require B;
})
.catch(e => {
console.log('Err in B', e)
})
// }
}
C();

Related

Nodejs promises: how to concatenate the resolved data within the Promise.all function [duplicate]

This question already has an answer here:
How to chain and share prior results with Promises [duplicate]
(1 answer)
Closed 5 years ago.
UPDATE: I FOUND AN "HOMEMADE" SOLUTION BY MY OWN, SCROLL DOWN TO THIS THIS SOLUTION!
When I'm doing NodeJS promises "manually" I have the option to pass the resolve value from the parent promise to it's child, or more precisely,
from the first promise to the second and so for and so on.
So, after first promise is done, I can continue using it's resolved data in the next promise. Sometimes this data is crucial for the whole process.
Such as sending a SMS via remote API, if the SMS will not be sent, I cannot be able to manage the next function.
I have tried the "shortcut" (*sorry if it does not meant to be a shortcut):
Promise.all.
Well it's nice, and it pops up the catched promise reject value first and if everything is okay it goes to finish.
But how can I preserve the resolved data in the same manner I have described above?
I will push here some code, so we can understand each other better:
Trying to do this using Promise.all
function test1() {
return new Promise(function(resolve, reject) {
resolve("test1");
});
}
function test2() {
return new Promise(function(resolve, reject) {
resolve("test2");
});
}
function test3() {
return new Promise(function(resolve, reject) {
resolve("test3");
});
}
Promise.all([test1, test2, test3].map(func => func())).then((result) => {
console.log('result: ', result);
}).catch((result) => {
console.log("Catched: ", result);
});
Trying to do this using the "manual" way:
function test1() {
return new Promise(function(resolve, reject) {
resolve("test1");
});
}
function test2(p) {
return new Promise(function(resolve, reject) {
resolve("test2");
});
}
function test3(p) {
return new Promise(function(resolve, reject) {
resolve("test3");
});
}
test1().then(function(result) {
return test2(result);
}).then(function(result) {
return test3();
}).then(function(result) {
console.log("Finished");
});
Now for the catch section if I am using the Promise.all function with the catch call back, it will rise the first catched match.
This is not good. Since I want to first tell the user he forgot filling email, later he has to be notified whether there was a problem sending SMS. But it does not working like that.
Actually, I can do it the manual way seperate for then and seperate for catch, but it is a mess.. And it is very easy to cause mistake in the syntax that will take hours to find.
********************** UPDATE - I FOUND AN HOMEMADE SOLUTIN!!! **********************
function test1() {
// some process return in overall as boolean
var overall = true;
if (overall)
return test2({test1: "is resolved"});
return catched("rejected on 1");
}
function test2(result) {
// some process return in overall as boolean
var overall = true;
result.test2 = "is resolved";
if (overall)
return test3(result);
return catched("rejected on 2");
}
function test3(result) {
// some process return in overall as boolean
var overall = true;
result.test3 = "is resolved";
if (overall)
return finished(result);
return catched("rejected on 3");
}
function finished(result) {
console.log(result);
}
function catched(result) {
console.log(result);
}
test1();
test1() will run first. After each of them done it goes to the next, syntax is simple, my homemade Promise.all is super simple thats the solution. Plus, if in future we want to add a middle function between test2 to test3, we should not think about adding it in the end handle.
Hmm.. probably that is it for now.
Hope it is clear to understand and help others!
THANKS :)
If your promises are all taking the result of the previous promise and returning to the next (like they do in your example), you can make the syntax much terser by doing something like this:
function test1() {
return new Promise(function(resolve, reject) {
resolve("test1");
});
}
function test2(p) {
return new Promise(function(resolve, reject) {
resolve(p + " test2");
});
}
function test3(p) {
return new Promise(function(resolve, reject) {
resolve(p + " test3");
});
}
test1()
.then(test2)
.then(test3)
.then(console.log)
First of all, the code blocks that you proposed are not equivalent.
The "manual" way that you show says, "run test1, then test2 with the value returned from test1, then run test3 with the value returned from test2.
The Promise.all way will fire all three functions, effectively, simultaneously and will not feed the return value from one to the next (promise.waterfall).
You may want to try async functions. They provide a more terse, synchronous looking script.
async function test1() {
return "test1";
}
async function test2(p) {
return `test2[${p}]`;
}
async function test3(p) {
return `test3[${p}]`;
}
(async function () {
let t1 = await test1();
console.log(`Look what I got: ${t1}`);
let t2 = await test2(t1);
console.log(`And here: ${t2}`);
let t3 = await test3(t2);
console.log(`Also here: ${t3}`);
return {
t1,
t2,
t3
};
}()).then(console.log);
Running this gives you the output:
Look what I got: test1
And here: test2[test1]
Also here: test3[test2[test1]]
{ t1: 'test1', t2: 'test2[test1]', t3: 'test3[test2[test1]]' }
In response to guest271314's comment:
Why would a "Promise chain" need to be broken?
I see the need to break the chain if you need to use an earlier resolution in a later sequence, but don't want to pass a conglomeration of all the results in the chain down. You can either nest callbacks which kind of defeats the point of Promises, or you could use async functions.
Consider:
const test1 = () => Promise.resolve("test1"),
test2 = (a) => Promise.resolve(`[${a}]test2`),
test3 = (a) => Promise.resolve(`[${a}]test3`),
test4 = (a) => Promise.resolve(`[${a}]test4`),
test5 = (a) => Promise.resolve(`[${a}]test5`),
greedy = (a, b) => Promise.resolve(`I need ${a} and ${b}`);
// Pass through
test1()
.then(test2)
.then(test3)
.then(test4)
.then(test5)
// Greedy should actually be getting the result of test1 and test5
.then(greedy)
.then(console.log);
// "Breaking" the chain
test1()
.then((a) => {
return test2(a)
.then(test3)
.then(test4)
.then(test5)
.then((b) => {
/*
* I have to nest if I want to give greedy
* the results of test1 and test5
*/
return greedy(a, b);
});
})
.then(console.log);
// Using async
(async function () {
const t1 = await test1(),
t2 = await test2(t1),
t3 = await test3(t2),
t4 = await test4(t3),
t5 = await test5(t4),
// I already have t1 and t5 populated
g = await greedy(t1, t5);
return g;
}()).then(console.log);
Prints:
I need [[[[test1]test2]test3]test4]test5 and undefined
I need test1 and [[[[test1]test2]test3]test4]test5
I need test1 and [[[[test1]test2]test3]test4]test5
I would recommend checking out the async functions zero298 recommends. For example (code not tested):
async function test1() {
return new Promise(function(resolve, reject) {
resolve("test1");
});
}
async function test2(p) {
return new Promise(function(resolve, reject) {
resolve("test2");
});
}
async function test3(p) {
return new Promise(function(resolve, reject) {
resolve("test3");
});
}
var test1 = await test1();
var test2 = await test2(test1);
var test3 = await test3();
console.log("Finished");

Passing rest of the codes as callback function

In a middle of already completed javascript function, you want to call a new async function. So you need to pass, "rest of the code" as a callback function as parameter to this new function.
function sample() {
alert("a bunch of codes");
alert("another a bunch of codes");
}
I have to change the function as below.
function sample() {
alert("a bunch of codes");
var cb = function () {
alert("another a bunch of codes");
};
newFunction(cb);
}
What if I want to add another function that has to wait first one ? Then I got numerous multiple levels of callback functions to the wait another..
So what is the best practice on ES5 ?
In ES5, just like you said you have to nest multiple callbacks inside each other.
Example:
function myFunction2(){
console.log(2);
let myFunction = () => {
console.log(1);
}
myFunction();
}
myFunction2();
// OUTPUT
// 2
// 1
ES6 also provides a new alternative, promises.
Example:
let myPromise = new Promise((resolve, reject) => {
setTimeout(function(){
resolve(1);
}, 250);
});
console.log(2);
myPromise.then((successMessage) => {
console.log(successMessage);
});
// OUTPUT
// 2
// 1
ES8 has provides an even better alternative(although it is just syntactic sugar based on promises) but you can use async functions with await.
Example:
function resolveAfter2Seconds(x) {
return new Promise(resolve => {
setTimeout(() => {
resolve(x);
}, 2000);
});
}
async function add1(x) {
const a = await resolveAfter2Seconds(20);
const b = await resolveAfter2Seconds(30);
return x + a + b;
}
add1(10).then(v => {
console.log(v); // prints 60 after 4 seconds.
});
Keep in mind though, that you probably need to use Babel to transpile your js in order to be compatible with all browsers.

How do I check if a JavaScript function returns a Promise?

Say I have two functions:
function f1() {
return new Promise<any>((resolve, reject) => {
resolve(true);
});
}
function f2() {
}
How do I know if f1 will return Promise and f2 will not?
Call the function, use instanceof
let possiblePromise = f1();
let isPromise = possiblePromise instanceof Promise;
So it's just about normalizing the output. Then run it through Promise.resolve() and co.
var possiblePromise = f1();
var certainPromise = Promise.resolve(possiblePromise).then(...);
or
var possiblePromises = [f1(), f2()];
Promise.all(possiblePromises).then(values => ...);
But you need to have a basic Idea of what these functions return. For Example: Both examples would fail with
function f3(){
return [somePromise, someOtherPromise];
}
var arrayOfPromisesOrValues = f3();
//here you'll still deal with an Array of Promises in then();
Promise.resolve(arrayOfPromisesOrValues).then(console.log);
//neither does this one recognize the nested Promises
Promise.all([ f1(), f2(), f3() ]).then(console.log);
//only these will do:
Promise.all(f3()).then(console.log);
Promise.all([ f1(), f2(), Promise.all(f3()) ]).then(console.log);
You need to know that f3 returns an Array of Promises or values and deal with it. Like you need to know that f1 and f2 return a value or a Promise of a value.
Bottom line: you need to have a basic knowledge of what a function returns to be able to run the result through the proper function to resolve the promises.
This can't be done.
You can, however, cast any function to a Promise-returning function. This is what I'd do here.
const toPromise = function (f) {
return function () {
return new Promise((resolve, reject) => {
const result = f.apply(null, Array.from(arguments));
try {
return result.then(resolve, reject); // promise.
} catch (e) {
if (e instanceof TypeError) {
resolve(result); // resolve naked value.
} else {
reject(e); // pass unhandled exception to caller.
}
}
});
};
};
const f = (x) => x;
const g = (x) => Promise.resolve(x);
const h = (x) => Promise.reject(x);
// Naked value.
toPromise(f)(1).then((x) => {console.log(x)});
// Resolved Promise.
toPromise(g)(2).then((x) => {console.log(x)});
// Rejected Promise.
toPromise(h)(3).catch((x) => {console.log(x)});
For NodeJS, we can use isPromise function from util/types built-in module. Link to docs here.
import { isPromise } from "util/types";
// assume the JS object you want to test is "obj"
isPromise(obj);

Patterns for returning same promise after calling method multiple times

Given i have a method that performs async operation and returns promise that gets resolve when that operation is done - how do i make sure that when called multiple times before that operation is done i get as a result same promise.
Edit when i call it after operation is done - i want to get new promise for current operation..
One option is to remember call ( memoize ) and eg track promise by a variable in outer scope
var p;
function test() {
if (p) return p;
p = new Promise(res) {
res();
p = null;
}
return p;
}
Any other nicer solutions?
The approach You're using here is called memoization and it's hard to come up with anything nicer.
What You might want to do is to split the tedious bits (save in outer scope, reuse, delete) from the function itself. Or even use a library:
import reusePromise from 'reuse-promise';
function test() {
return new Promise(function (res) {
res();
});
}
const reusableTest = reusePromise(test);
Full example
"use strict";
const reusePromise = require('reuse-promise').default;
let i = 0;
function test() {
return new Promise(resolve => {
setTimeout(() => resolve(i++), 100);
});
}
const reusableTest = reusePromise(test);
reusableTest().then(console.log).catch(console.err);
reusableTest().then(console.log).catch(console.err);
reusableTest().then(console.log).catch(console.err);
setTimeout(() => {
reusableTest().then(console.log).catch(console.err);
reusableTest().then(console.log).catch(console.err);
reusableTest().then(console.log).catch(console.err);
}, 200);
// output: 0 0 0 1 1 1

nodejs: wait for other methods to finish before executing

say I have 2 methods:
function A(callback) { ... }
function B(callback) { ... }
I want to execute:
function C();
after both A and B are finished.
what we usually do is to put function C in the callback like:
A(function() {
B(function() {
C();
});
});
now if both A and B takes a long time, I don't want B to execute after A has been finished. instead I want to start them at the same time to enhance performance.
what I'm thinking is to implement something like a semaphore (not really a semaphore of course), it fires an event after both A and B are finished. so that I can call C from within the event.
what I want to know is, is there any library implemented the above function already? I believe I'm not the first one who wants to do it.
any help is appreciated.
To expand on my comment...
async is a commonly used asynchronous flow control library for Node.js.
Its async.parallel() would probably do well for this:
async.parallel([
function(done) {
A(function () {
done(null);
});
},
function(done) {
B(function () {
done(null);
});
}
], function (err) {
C();
});
It's possible that this can be shortened, but it depends on how each function interact with callbacks and whether they follow the common Node.js pattern of error-first callbacks:
async.parallel([A, B], C);
For the sake of completeness and as mentioned above, the same result can be achieved using an external object to hold completion state where both A() and B() check to see if the other has completed and if so, invokes C(). As in:
var results={};
function onComplete(){
if(results['A'] && results['B'] && !results['C']) {
C();
}
}
function A(){
// ...
results['A']=true;
onComplete();
}
function B(){
// ...
results['B']=true;
onComplete();
}
The results object can be replaced by adding a 'isComplete' flag to both A() and B(), as in:
function A(){
// ...
A.isComplete=true;
onComplete();
}
And modifying onComplete to check this new flag:
function onComplete(){
if(A.isComplete && ...
}
Or, the same using events:
var util=require('util'),
events=require('events'),
EventEmitter=events.EventEmitter;
function F(){
EventEmitter.call(this); // init ancestor
}
util.inherits(F,EventEmitter); // assign ancestor
F.prototype.A=function(){
var self=this;
console.log('running A()');
setTimeout(function(){ // simulate long running code - 5 seconds
F.prototype.A.isComplete=true;
self.emit('complete','A');
},5000);
};
F.prototype.B=function(){
var self=this;
console.log('running B()');
setTimeout(function(){ // simulate long running code - 3 seconds
F.prototype.B.isComplete=true;
self.emit('complete','B');
},3000);
};
F.prototype.C=function(){
console.log('running C()');
};
var f=new F;
f.on('complete',function(which){ // onComplete handler
console.log(which+'() is complete');
if(F.prototype.A.isComplete && F.prototype.B.isComplete){
f.C();
}
});
// start it up
f.A();
f.B();
which, when run, will produce:
>node example.js
running A()
running B()
B() is complete
A() is complete
running C()
>
async.parallel([
function(){ ... },
function(){ ... }
], callback);
from: https://github.com/caolan/async
so in your case:
async.parallel([funcA,funcB],funcC);
//function definitions
function funcA() {...}
function funcB() {...}
function funcC() {...}
without modules i guess it would look something like this:
var numFuncs = 2;
A(D);
B(D);
and then
function D() {
if(numFuncs==0){ C(); } else {
numFuncs--;
}}
or like this:
A(D(C));
B(D(C));
function D() {
var x = process.funcsToCall= process.funcsToCall || {};
var f=arguments[0];
(!x.hasOwnProperty(f.name))?x[f.name]=0:x[f.name]++;
return function(){
(x[f.name]==0)?f():x[f.name]--;
}
}
If you're running on ES6, you can use Promise.all. Here is the example code rewritten:
Promise.all([
new Promise((resolve) => A(() => resolve())),
new Promise((resolve) => B(() => resolve())),
]).then(() => {
C()
}).catch(err => {
// handle errors from function A, B and C
})
we can aync and await for this purpose for example:
async function Foo(){
// function logic
}
and this Foo function as:
await Foo();

Categories

Resources