Observable wait for a method to complete - javascript

I want to run method A and B parallelly and once they both are done, I want to run method C.
How can I achieve this in javascript using Observable?
main() {
this.methodA();
if(some_condition) this.methodB();
this.methodC();
}
methodA() {
setTimeout( () => { console.log("doing some work A"); }, 500);
}
methodB() {
setTimeout( () => { console.log("doing some work B"); }, 250);
}
methodC() {
console.log("should be the last...");
}
expected output (if some_condition is false):
doing some work A
should be the last...
expected output (if some_condition is true):
doing some work A
doing some work B
should be the last...
expected output (if some_condition is true):
doing some work B
doing some work A
should be the last...

While I agree that it seems like your spec could probably best be accomplished using promises, I figured I'd give you an answer using Observables, seeing how that's what you asked for.
Essentially, just use the merge operator, make methodA() and methodB() return observables, and call methodC() when they're all complete:
var some_condition = true
function main() {
let test$ = a()
if (some_condition) test$ = test$.merge(b())
test$.subscribe(console.log, null, c)
}
function a() {
return Rx.Observable.timer(500)
.mapTo('doing some work A')
}
function b() {
return Rx.Observable.timer(250)
.mapTo('doing some work B')
}
function c() {
console.log('should be the last...')
}
main()
This logs:
doing some work B
doing some work A
should be the last...

Your best bet would be to use a Promise which allow functions to be run asynchronously and then trigger some function when they have completed. The benefit here is that promises can be composed so that you can wait on any or all of them to resolve before doing some work.

Use ES7s async functions / await :
async function main() {
await this.methodA();
if(true || some_condition) await this.methodB();
await this.methodC();
}
async function methodA() {
console.log("doing some work A");
await timer(1000);
console.log("finished A");
}
async function methodB() {
console.log("doing some work B");
await timer(1000);
console.log("finished B");
}
async function methodC() {
console.log("should be the last...");
}
function timer(time){
return new Promise(function(res){
setTimeout(res,time);
});
}
main();

People may forget the hidden gem of Observable.if().
Observable.if(condition, thenSource, [elseSource]) takes in 3 arguments. First argument is the boolean condition, second argument is an Observable to be emitted if condition is true, and the last one being an array of else source, that is to be emmitted if condition is false.
If you want to fully Observable-lised your code, you can do it this way:
1. Have all your methods return an Observable:
const methodA = () => {
return Observable
.timer(500)
.do(() => {
//do your work A here
console.log('doing some work A');
})
};
const methodB = () => {
return Observable
.timer(250)
.do(() => {
//do your work B here
console.log('doing some work B');
})
};
const methodC = () => {
console.log("should be the last...");
};
2. Use Observable.if() to resolve the streams
Now, in your main, simply use an Observable.if() to check if the condition is true, and emit the Observable accordingly:
const main = () => {
let some_condition = true;
Observable
.if(
//a function that returns true or false
() => some_condition,
//Observable to emitif condition is true
methodA().switchMap(() => methodB()),
//Observable to emit if condition is false
methodA()
)
.subscribe(() => {}, error => {}, methodC)
};
Here is the working JSBin for you.
More on Observable.if(): https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/if.md

As #CozyAzure said, Observable.if() is what you want. Make sure to make use of Observable.merge() and Observable.concat().
const methodA = () => Observable.timer(500).mapTo("doing some work A")
const methodB = () => Observable.timer(250).mapTo("doing some work B")
const methodC = () => Observable.of("should be the last...")
const main = (some_condition) =>
Observable.if(
() => some_condition,
methodA().merge(methodB()),
methodA()
)
.concat(methodC())
.subscribe(console.log)
main(true)
Here's the example in jsfiddle
If you're working with promises, you may want to defer the creation of your promise. For example,
const methodA = () => Observable.timer(500).mapTo("doing some work A")
const methodB = () => Observable.timer(250).mapTo("doing some work B")
const methodC = () => Promise.resolve("should be the last...")
Observable.merge(methodA(), methodB())
.concat(Observable.defer(() => methodC())
.subscribe(console.log)

Related

How can I chain functions asynchronously using JavaScript?

I was asked to create such an object called foo that can chain the function log and wait.
For example:
foo.log('breakfast').wait(3000).log('lunch').wait(3000).log('dinner');
In this scenario it prints breakfast first, waits 3 seconds, prints lunch, and then after 3 seconds it prints dinner.
I tried something like this, but it doesn't work. What did I miss?
var foo = {
log: function(text){
console.log(text);
return foo;
},
wait: function(time) {
setTimeout(function() {
return foo;
}, time);
}
}
foo.log('breakfast').wait(3000).log('lunch').wait(3000).log('dinner');
It's always better to use promises. An implementation of this functionality could be;
class Foo {
constructor(){
this.promise = Promise.resolve();
}
log(txt){
this.promise = this.promise.then(_ => console.log(txt))
return this;
}
wait(ms){
this.promise = this.promise.then(_ => new Promise(v => setTimeout(v,ms)));
return this;
}
}
var foo = new Foo();
foo.log("happy").wait(1000).log("new").wait(1000).log("year");
For the record, Redu's excellent answer without the class sugar.
See also
const foo = {
promise: Promise.resolve(),
log(txt) {
this.promise.then(_ => console.log(txt));
return this;
},
wait(ms) {
this.promise = this.promise.then(_ => new Promise(v => setTimeout(v, ms)));
return this;
}
};
// OR
const Foo = (defaultMs = 1000) => {
let promised = Promise.resolve();
return {
log(txt) {
promised.then(_ => console.log(txt));
return this;
},
wait: function(ms) {
promised = promised.then( _=>
new Promise( rs => setTimeout(rs, ms || defaultMs) ) );
return this;
}
};
};
foo.log("Happy").wait(1000).log("new").wait(1000).log("year");
Foo().wait(3000)
.log(`** From Foo ;)`).log(`Happy`).wait().log("new").wait().log("year");
Place the call to wait inside the previous one, and as the last item, like a recursive function.
meals=['breakfast','elevenses','lunch','afternoon tea','dinner','supper'];
c=0;
wait=t=>{setTimeout(function() {
if (c<meals.length) document.write(meals[c++],'<br>');wait(500);
}, t);}
wait(500);
I was inspired by #James 's solution, which is partially wrong because the log messages are in the reverse order, but he does not use Promises. I still think that #Redu 's solution should be the accepted one (after all if you can use Promises, that is perfect), but this one is interesting too in my opinion:
const foo1 = {
incrementalTimeout: 0,
nextActions: [],
log(text) {
const textLog = () => { console.log(text); };
if (this.incrementalTimeout == 0)
textLog();
else
this.nextActions.push(textLog);
return this;
},
wait(time) {
let newObj = {...this, incrementalTimeout: this.incrementalTimeout + time, nextActions: []};
setTimeout(() => { newObj.nextActions.forEach((action) => action()); } , newObj.incrementalTimeout);
return newObj;
}
}
foo1.log('breakfast').wait(1000).log('lunch').wait(3000).log('dinner');
The idea is that I do not immediately log text but I push a lambda with the console.log in an array that is going to be called after the correct timeout expires.
I run all the log and wait operations one after the other, but I keep track of the seconds to wait before executing the actions. Every time a new wait is called, the incrementalTimeout is increased by time. To keep the nextActions belonging to different time frames separated, I return a newObj every time, more or less like #James does.
Shorter, within Promise (not recommended).
Promise.prototype.log = function(txt) {
return this.then(() => console.log(txt))
}
Promise.prototype.wait = function(ms) {
return this.then(() => new Promise(res => setTimeout(res, ms)))
}
var foo = Promise.resolve()
foo.log('breakfast').wait(3000).log('lunch').wait(3000).log('dinner')
You can do it without promises:
const foo = {
log(text) {
return {...foo, start: () => {
this.start();
console.log(text);
}};
},
wait(time) {
return {...foo, start: () => {
setTimeout(this.start, time);
}};
},
start() {}
};
foo.log('breakfast').wait(3000).log('lunch').wait(3000).log('dinner').start();
The functions foo.log() and foo.wait() return immediately, returning a modified clone of foo. A clone is made using {...foo}, but with the start() function modified so that it calls the caller's this.start() followed by the new operation. When the chain is complete you call start() to start the actions.

Chain of promises in javascript with ajax calls

I have a function (f1) which I want to get called only after two ajax calls (say a1 and a2) are done. a2 should be called only after a1 is done. Following is the sequence of operation =
$.when(a1 and a2) {
f1
}
I tried the following code snippet -
$.when(a1a2()).done(function(){
f1();
}
var a1a2 = function(){
return $.when(a1()).done(function() {
if (<check for a few variables a1 sets>) {
// another ajax call for which f1 should wait
return a2();
} else {
// random function for which f1 shouldn't wait
f2();
}
});
}
In the above code, f1 is waiting for a1 to finish but it is not waiting for a2 to finish.
I tried the following code snippet as well (but this also just waits for a1 to finish) -
var a1a2 = function(){
var retVal = new Promise(function(){
a1().then(function(){
if (<check for a few variables a1 sets>) {
return a2();
} else {
// random function for which f1 shouldn't wait
f2();
}
});
});
}
I have looked at other similar questions but an not able to devise a solution. Can someone please help?
First of all, your Promise code is faulty, because you're not creating a Promise correctly
new Promise(function(resolve, reject) {
// in here you call resolve or reject otherwise Promise is forever pending
});
However, since a1 returns a Promise (as do all the functions, I'm assuming) you don't need to create a promise
So, your code would be
a1()
.then(function() {
if (somecondition == true) {
return a2();
} else {
f2(); // since there's no return here, there's no "wait" for the promise f2 returns
}
})
.then(function() {
return f1();
})
To illustrate the above, here's your code once with condition true, and then with condition false
Take note of the "time stamps" for the console output
// dummy code to set up some promises
const dummy = (x, d=1000) => {
console.log(performance.now(), 'start', x);
return new Promise(resolve => setTimeout(() => {
console.log(performance.now(), 'end', x);
resolve(x);
}, d));
};
const a1 = () => dummy('a1');
const a2 = () => dummy('a2');
const f1 = () => dummy('f1');
const f2 = () => dummy('f2', 3000);
// end dummy code
console.log('wait for a2');
a1()
.then(function() {
if (true) {
return a2();
} else {
// random function for which f1 shouldn't wait
f2();
}
})
.then(function() {
return f1();
})
.then(() => {
console.log('dont wait for f2');
a1()
.then(function() {
if (false) {
return a2();
} else {
// random function for which f1 shouldn't wait
f2();
}
})
.then(function() {
f1();
});
});
However! If f2 is a function that has no asynchrony then there is no way * to prevent f1 from being called after f2 finish - because that's how javascript works
* - I guess you could put it in a setTimeout, then f2 would execute after f1 begins (again, assuming f1 has some asynchrony, otherwise f2 would begin after f1 ends)
Call a1 to get its Promise, then call Promise.all on a1 and a1 chained with a2:
const a1Prom = a1();
Promise.all([
a1Prom,
a1Prom.then(a2)
])
.then(f1);
console.log('script start');
const delay = ms => new Promise(res => setTimeout(res, ms));
const a1 = () => delay(1000).then(() => console.log('a1 done'));
const a2 = () => {
console.log('a2 starting');
return delay(1000).then(() => console.log('a2 done'));
};
const f1 = () => console.log('f1 starting');
const a1Prom = a1();
Promise.all([
a1Prom,
a1Prom.then(a2)
])
.then(f1);
I'm not familiar with the when/done syntax, but this is a correction for your second snippet. A big hint I would give is that using new Promise is 90% of the time a bad idea.
var a1a2 = function(){
var retVal = a1().then(function(){
if (<check for a few variables a1 sets>) {
return a2();
} else {
// random function for which f1 shouldn't wait
f2();
}
});
});
function a1() {
return new Promise(resolve => {
resolve();
});
}
function a2() {
return new Promise(resolve => {
resolve();
});
}
function f1() {
// this runs when the a1 and a2 is resolved.
}
// this is the call method. there are so many approach in your question first is chaining of promise.
function CallMethod(){
a1().then(function () { // you can optionally pass data here via the resolve in the promise
return a2();
}).then(function () { // same here you can pass optional data here.
// here the a1 and a2 is resolved you can call the f1() now.
f1();
});
}
// second one is called Promise.all()
function CallMethod() {
Promise.all([a1(), a2()]).then(data => { // this is the optional data passed in the resolve base on the index of the function promises.
var firstResolve = data[0]; // resolved data of a1();
var secondResolve = data[1]; // resolved data of a2();
})
}

Is it logical to use async resolve ever?

I am new to Promises, so I am wondering if this is fine:
this.someFunc()
.then(() => {
alert('loaded')
});
someFunc() = () => {
return new Promise(async(resolve) => {
let obj = await AsyncStorage.getItem('some_val');
//do stuff with obj
resolve('yay, everything's done');
});
};
Or should I always ALWAYS use then with Promises like
return new Promise((resolve) => {
AsyncStorage.getItem('some_val')
.then((obj) => {
// do stuff with obj
resolve('yay')
});
? Is the first approach anti-pattern like? Is it "wrong"? And why?
Adding the async keyword to a function makes it return a Promise.
Ignoring that promise and wrapping it in another one doesn't really make sense. It makes the code confusing to people trying to maintain it.
You could rewrite it as:
someFunc() = async () => {
let obj = await AsyncStorage.getItem('some_val');
//do stuff with obj
return 'yay, everything's done';
};
I just refactored your codes by removing unnecessary things.
someFunc() = async () => {
// when you declare arrow function, it should not have () with its name
// put async before arguments to enable await in the code block below
return new Promise(async(resolve) => {
// you don't need this line because of async and await
// await returns data wrapped by Promise
// as soon as this Promise is resolved or rejected
let obj = await AsyncStorage.getItem('some_val');
// 'await' will return the item that is wrapped by Promise.
// So, you can access the item(the returned data) in this code block.
// but if you want this 'someFunc' to return 'obj'
//do stuff with obj
resolve("yay, everything's done");
// you don't need this line because 'await' will take care of
// 'AsyncStorage.getItem('some_val')'
});
};
After refactoring, you will have code like below.
someFunc = async () => {
// One of the benefit of async and await is that you can use try/catch block.
// So, you can easily console out error without by chaining multiple '.catch' after
// every '.then'.
try{
let obj = await AsyncStorage.getItem('some_val');
// 'obj' will be the value of item 'some_val'
// do stuff with obj
} catch(error) {
console.log(error);
}
return obj; //this will return resolved Promise because of await.
};
If you want to use this obj in another function, you can do something like below.
anotherFunc = async () => {
try{
const resolvedObj = await someFunc();
// Since someFunc will return resolved Promise that wraps the data from
// AsyncStorage, await will assign the data to 'resolvedObj'
// However, if you return 'resolvedObj', this will be 'PromiseĀ {<resolved>:
// dataFromAsyncStorage}'
// do stuff with 'resolvedObj'
} catch(error) {
console.log(error);
}
}

How to wait for a jQuery toggle to finish before running another function?

I am missing something really obvious but I can't make one of my methods to wait until a jQuery toggle is done.
I have the following three methods:
const toggleContainer = function() {
addAnswerContainer.toggle("slow");
};
const toggleButtons = function() {
submitAnswerBtn.toggle("slow");
cancelAnswerBtn.toggle("slow");
};
const changeText = function() {
if( addAnswerContainer.is(":hidden") ) {
addOwnAnswerBtn.text("Add your own answer" );
}
else {
addOwnAnswerBtn.text("Hide adding answer");
}
};
All I want to do is to have something like this to work:
const toggleUI = function() {
toggleContainer();
toggleButtons();
changeText(); // executes too fast
};
The problem is that changeText() executes too quickly and every time it checks, the addAnswerContainer.is(":hidden") returns false, unless I setTimeout it for some seconds, which is not what I want. I tried with callbacks, also with Promises, but I just can't make it work. How can I make the changeText() to wait, until the addAnswerContainer.toggle() has done its job?
.toggle( [duration ] [, complete ] )
The optional second argument is the function to cal once the animation is done, so just call toggle with the desired callback. Promises are the best way to do something like this:
const toggleContainer = () => new Promise(resolve =>
addAnswerContainer.toggle("slow", resolve)
);
const toggleButtons = () => new Promise(resolve => {
submitAnswerBtn.toggle("slow");
cancelAnswerBtn.toggle("slow", resolve);
});
const changeText = function() {
if( addAnswerContainer.is(":hidden") ) {
addOwnAnswerBtn.text("Add your own answer" );
}
else {
addOwnAnswerBtn.text("Hide adding answer");
}
};
Then call .then on the promises to chain asynchronous functions:
const toggleUI = function() {
toggleContainer()
.then(toggleButtons)
.then(changeText);
};
Or if you want toggleContainer and toggleButtons to run at once, and only wait before changeText:
const toggleUI = function() {
toggleContainer()
toggleButtons()
.then(changeText);
};
I may suggest a simple but smart approach following your if-else statements like so:
const toggleContainer = function() {
addAnswerContainer.toggle("slow");
return true;
};
const toggleButtons = function() {
submitAnswerBtn.toggle("slow");
cancelAnswerBtn.toggle("slow");
};
const ToggleIfTrue = () => {
if(addAnswerContainer.is(":hidden") && toggleContainer) {
addOwnAnswerBtn.text("Add your own answer" );
}
else if (addAnswerContainer.is(!":hidden") && toggleContainer) {
addOwnAnswerBtn.text("Hide adding answer");
}
};
So this way the inner if-else statement only runs if the addAnswerContainer is set as hidden and the other way around with the final else if statement
Can return $.when promise from the toggle function and use promise() of element objects to be resolved in $.when.
call next step in next then() of promise chain
Simplified demo
const toggleComplete = function(){
console.log('finished animation id #', this.id)
}
const toggleButtons = function (){
// toggleComplete only needed for this demo
let p1=$('#1').toggle('slow',toggleComplete).promise()
let p2=$('#2').toggle('slow',toggleComplete).promise()
return $.when(p1,p2)
}
toggleButtons().then(_=>console.log('all animations done')/* change text here*/)
div{display:none}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="1">
One
</div>
<div id="2">
Two
</div>

Async transactions in javascript

First of all rollback is something that I do not care about.
I would like to be able to lock a sequence of async functions/promises/tasks (let's call it a "transaction") with a name/id (or array of names), so that they happen in sequence, and so that any other "transaction" with the same name(s) that are run by another part of the system are delayed from starting until the running transaction using the same name(s) has completed. So it basically is queueing the sequences of async tasks, or "transaction"s.
Here is some example code of the situation:
function a()
{
// do stuff
return new Promise(/*...*/);
}
function b()
{
// do stuff
return new Promise(/*...*/);
}
function c()
{
// do stuff
return a.then(() => b());
}
Now at any time the system could call a, b, or c, and when it does I don't want c and b running at the same time, but obvious c depends on b.
I've been looking for a package on npm to help with this but I haven't found anything, I wonder if anyone can suggest something that I might have missed that would help with this?
I think gulp tasks can help you out of the box. This guarantees that c always run after b and so b after a
const gulp = require('gulp');
gulp.task('a', done => {
// do stuff
console.log('a');
done();
});
gulp.task('b', ['a'], done => {
// do stuff
console.log('b');
done();
});
gulp.task('c', ['b'], done => {
// do more stuff
console.log('c');
done();
});
gulp.start('c'); // Logs a, b, c
Try it!
You could write your own little transaction manager.
const transactions = {};
function doTransaction(name, promiseFunc) {
transactions[name] = (transactions[name] || Promise.resolve()).then(promiseFunc);
}
Use async/await and have babel transpile it. Async Babel Docs
function a()
{
// do stuff
return new Promise(/*...*/);
}
async function b()
{
const aData = await a();
// do stuff
return new Promise(/*...*/);
}
async function c()
{
const bData = await b();
// do stuff
return bData;
}
You can go for https://github.com/Reactive-Extensions/RxJS
They have many functions to handle single/multiple/dependent/parallel async calls.
function a()
{
// do stuff
return new Promise(/*...*/);
}
function b()
{
// do stuff
return new Promise(/*...*/);
}
function c()
{
// do stuff
return new Value;
}
a().then(function(data_a) {
// you can make use of return value (which is data_a) here or as an argument for function b or even function c
b().then(function(data_b) {
// you can make use of return value (which is data_b) here or as an argument for function c
c().then(function(data_c) {
// do your coding here
});
});
});
you can check this link for reference : https://spring.io/understanding/javascript-promises
Ok, here's my take.
You use a wrapper for your function b which returns and object with 2 methods: doCall and wait. The wrapper should be called only once.
doCall will call your function and trace its completion for the wait() function.
wait() will wait for the completion and always resolve when doCall() finishes
Now for the code, also on CodePen (see developer console):
function wrapPromiseFn(fn) {
var prev = null;
var doCall = function() {
var retValue;
prev = new Promise(function(resolve, reject) {
retValue = fn();
retValue.then(function(result) {
resolve(true);
});
retValue.catch(function(result) {
resolve(true);
});
});
return retValue;
};
var wait = function() {
return prev ? prev : Promise.resolve(false);
};
return {
doCall: doCall,
wait: wait
};
}
function a() {
return Promise.resolve(42);
}
function b() {
//return Promise.reject(new Date());
return Promise.resolve(new Date().getTime());
}
var wrappedB = wrapPromiseFn(b);
function doStuff() {
return wrappedB.wait().then(function(didWait) {
return a().then(function(_a) {
return wrappedB.doCall().then(function(_b) {
console.log("didWait, a, b: ", didWait, _a, _b);
});
});
});
}
//run it twice to prove it did wait
doStuff().then(doStuff)
It proves the concept, of course it would need some polish to pass arguments from doCall to the wrapped function.

Categories