Javascript: (implicitely) call a final method after method chains - javascript

I'm looking for a way to call a method "implicitely" after a chain (fluent interface pattern).
I already have something like
class Foobar {
methodA() {
// update state
return this;
}
methodB() {
// update state
return this;
}
applyChanges() {
// persist the state at this point
}
}
And on the usage:
new Foobar().methodA().methodB().applyChanges();
The thing here, is that I don't know in advance if users of the class will use only one chain method or two (or even zero) and for "developer experience", I didn't want to expose the applyChanges method.
Is there a way to perform that? Or even another pattern I could implement. For example, I thought I could stack the method calls first, but I still won't know when to stop and execute the applyChanges method :(

If the applyChanges operation were async (and I'm guessing the "persisting" might need something asynchronous anyway), it would be easy: you could have a then method like this:
async applyChanges () { /*...*/ }
then (success, failure) {
return this.applyChanges().then(success, failure)
}
Then awaiting your object would implicitly apply changes and wait for them to be done (await new Foobar().methodA().methodB()), because async/await uses promises under the hood, so await x will cause x.then to be called if existing. This is how Mongoose query objects behave for example.

Ok so I actually got away with it thanks to Polly. Because it lazily evaluates the http handler i'm building, it gets the "right version" of it when the HTTP requests happen :)

Related

Get return value from subcribe on Observable

Using RxJS 5.0.0-rc.1, I'm trying to communicate my Observer and Observable in a way similar to how generators/iterators work by exchanging data using yield and .next(). The intention is to get a hold of what a call to .subscribe returns and modify/update following values in my observable stream depending on that.
I'm not entirely sure if this is, at all, possible. Though, I found out that you can catch exceptions thrown on .subscribe callbacks. The following snippets prints out "Boom!":
var source = Observable.create((observer) => {
try {
observer.next(42);
} catch (e) {
// This will catch the Error
// thrown on the subscriber
console.log(e.message);
}
observer.complete();
});
source.subscribe(() => {
throw new Error('Boom!');
});
So, what if instead of throwing, the subscriber returns a value? Is there a way for the Observable to retrieve it? Perhaps I'm approaching this the wrong way. If so, what's the "reactive" way of doing things in this scenario?
Many thanks.
EDIT
One possible way I came up with is by providing a callback function on every item in the stream. Something like:
var source = Observable.create((observer) => {
// This will print "{ success: true }"
observer.next({ value: 42, reply: console.log });
observer.complete();
});
source.subscribe(({ value, reply }) => {
console.log('Got', value);
return reply({ success: true });
});
Any other thoughts?
EDIT 2
Since my original question brought some confusion on what I was trying to achieve, I'll describe my real world scenario. I'm writing the API of a module for managing messages through queues (much like a simplified, in memory, AMQP-RPC mechanism) and I though RxJS would be a good fit.
It works like you would expect: a Publisher pushes messages to a queue, which get delivered to a Consumer. In term, the Consumer can reply to the Publisher, which can listen to that response if it's interested.
In an ideal scenario, the API would look something like this:
Consumer().consume('some.pattern')
.subscribe(function(msg) {
// Do something with `msg`
console.log(msg.foo);
return { ok: true };
});
Publisher().publish('some.pattern', { foo: 42 })
// (optional) `.subscribe()` to get reply from Consumer
That example would print 42.
The logic for replying to the Publisher lies within the Consumer function. But the actual response comes from the .subscribe() callback. Which leads me to my original question: how should I go about fetching that returned value from the creator of the stream?
Think of Consumer#consume() as:
/**
* Returns an async handler that gets invoked every time
* a new message matching the pattern of this consumer
* arrives.
*/
function waitOnMessage(observer) {
return function(msg) {
observer.next(msg);
// Conceptually, I'd like the returned
// object from `.subscribe()` to be available
// in this scope, somehow.
// That would allow me to go like:
// `sendToQueue(pubQueue, response);`
}
}
return Observable.create((observer) => {
queue.consume(waitOnMessage(observer));
});
Does it make any more sense?
There are indeed similarities between generators and observables. As you can see here, observables (asynchronous sequence of values) are the asynchronous version of iterables (synchronous sequence of values).
Now, a generator is a function which returns an Iterable. However, Rxjs Observable encloses both a generator - a.k.a producer (that you execute/start by calling subscribe) and the produced asynchronous sequence of values (that you observe by passing an Observer object). And the subscribe call returns a Disposable which allows you to stop receiving values (disconnect). So while generators and observables are dual concepts, the APIs to use them differ.
You cannot do two-way communication by default with the rxjs observable API. You probably could manage to do it by constructing yourself the back channel through subjects (note that you MUST have an initial value to kick off the cycle).
var backChannel = Rx.Subject();
backChannel.startWith(initialValue).concatMap(generateValue)
.subscribe(function observer(value){
// Do whatever
// pass a value through the backChannel
backChannel.next(someValue)
})
// generateValue is a function which takes a value from the back channel
// and returns a promise with the next value to be consumed by the observer.
You could consider wrapping that with :
function twoWayObsFactory (yield, initialValue) {
var backChannel = Rx.BehaviorSubject(initialValue);
var next = backChannel.next.bind(backChannel);
return {
subscribe : function (observer) {
var disposable = backChannel.concatMap(yield)
.subscribe(function(x) {
observer(next, x);
});
return {
dispose : function (){disposable.dispose(); backChannel.dispose();}
}
}
}
}
// Note that the observer is now taking an additional parameter in its signature
// for instance
// observer = function (next, yieldedValue) {
// doSomething(yieldedValue);
// next(anotherValue);
// }
// Note also that `next` is synchronous, as such you should avoir sequences
// of back-and-forth communication that is too long. If your `yield` function
// would be synchronous, you might run into stack overflow errors.
// All the same, the `next` function call should be the last line, so order of
// execution in your program is the same independently of the synchronicity of
// the `yield` function
Otherwise, the behaviour you describe seems to be that of an asynchronous generator. I never used such, but as this is a proposal for some future version of javascript, I think you can
already start trying it out with Babel (cf. https://github.com/tc39/proposal-async-iteration).
EDIT :
If you are looking for a loop-back mechanism (less general purpose approach but could very well fits your use case, if what you want to do is simple enough), the expand operator could help. To understand its behaviour, please check the doc, and the following answers on SO for examples of use in concrete contexts:
RxJS: Backpressure with switchMap producing N values
Circular Dependencies with RxJS. Modeling spores
How to build an rx poller that waits some interval AFTER the previous ajax promise resolves?
Basically expand allows you to both emit a value downstream and feed that value back at the same time in your producer.

AngularJS - "Chaining promises" with other functions in between

So I have this problem. I'm fairly new to angular and I've been told to modify a directive which manages forms to make the submit button disabled then enabled again when all the work is done.
Since the functions being called usually have async calls, simply adding code sequentially doesn't work.
var ngSubmit = function() {
vm.disabled = true;
$scope.ngSubmitFunction();
vm.disabled = false;
}
Button is enabled before async calls under ngSubmitFunction() finish.
So I thought a promise would fix that and made something like:
var promise = function() {
return $q(function (resolve) {$scope.ngSubmitFunction()});
}
var ngSubmit = function() {
vm.disabled = true;
promise().then(function() {
vm.disabled = false;
});
}
This doesn't output any error but never enables the button again (.then is never called).
Tried different kind of promises declaration, all with the same result, except for this one:
$scope.submitPromise = function() {
return $q.when($scope.ngSubmitFunction());
}
This does call .then function, but again, doesn't wait for any child async function to finish. '.then' is called instantly, like the sequential version.
Have in mind that I don't know what's under ngSubmitFunction(). It is used by dozens developers and it may contain from 0 to multiple async calls. But typical scenario is something like:
ngSubmitFunction() calls func()
-- func() decides wether to call create() or update()
-- -- update() calls a elementFactory.update() which is an async call
-- -- -- elementFactory.update().then(function()) is called when finished.
-- -- -- -- At THIS point, I should enable the button again.
How can I achieve this? is there a way to chain promises with non-promises functions in between? or another way to make a code only execute when everything else is done? I thought about creating an event at DataFactory when an async call is over but if the update() function was calling to more than one async call this wouldn't work.
If you are using promises, your async functions should return promises, if they do it should work like this:
var ngSubmit = function() {
vm.disabled = true;
$scope.ngSubmitFunction().then(() => {
vm.disabled = false;
});
}
I don't know what's under ngSubmitFunction()
Then you're out of luck, promises won't help you here. Promises or $q.when cannot magically inspect the call and see what asynchronous things it started or even wait for them - ngSubmitFunction() needs to return a promise for its asynchronous results itself.
You need to make every function in your codebase which (possibly) does something asynchronous that needs to be awaitable return a promise. There's no way around this.
Well, in case someone wonders, we haven't found a solution to that (there probably isn't). So we will go with the adding returns to all the chain of functions to make sure the ngSubmitFunction recieves a promise and therefor can wait for it to finish before calling '.then'. Not only this makes it work for the cases where there's only one promise implied but it is also a good programming practice.
Cases with more than one promise are scarce so we will treat them individually on the controller itself.
Thank you all for your comments.

Is an asynchronous class in Javascript a good idea?

I'm looking to create a class which retrieves information from a database and sets user variables appropriately.
My issue is that, the database API I am using is asynchronous and this becomes an issue because the class becomes accessible before it has been 'fully constructed' (that is to say the constructor function has returned).
Desired synchronous approach
class User{
constructor(sql){
this.name = Database.selectQuery(sql);
}
}
var x = new User(statement);
console.log(x.name); // returns name
Current asynchronous approach
class User{
constructor(sql){
Database.selectQuery(sql, this, function(data, obj){
obj.name = data;
});
}
}
var x = new User(statement);
console.log(x.name); // returns undefined
I'm aware I can add a callback to my constructor in order to make it work asynchronously but is this a good idea?
You will quickly find that all async operations must be able to communicate to the caller when they are complete because they complete some indeterminate time in the future. This means they either need to accept a callback or they need to return a promise.
While you can pass a callback to a constructor, you can't return a promise from a constructor (it has to return the object) so it is generally not recommended to put an async operation in the constructor. Instead, the code flow by the caller is more straightforward if you construct the object and THEN call an async method on the object.
As your example clearly shows, you have created no way for the caller of your User object to know when the async operation in the constructor is complete and thus, there is no reliable way for the caller to use the results of that operation. That's not just a bad idea, but a complete non-starter. You would be forcing the caller to set some sort of timeout and either guess when the async operation is done or poll looking for completion - both bad things to do.
The more common design approach is to separate out the async operation into a method:
class User{
constructor(){ }
select(sql, callback) {
Database.selectQuery(sql, this, function(data, obj){
obj.name = data;
callback();
});
}
}
var x = new User();
x.select(statement, function() {
console.log(x.name);
});
The modern way of designing async interfaces is really moving to promises now and they offer significant advantages in error handling and in coordinating multiple async operations so that would really be my recommendation for an interface.
Your interface above probably also needs error handling.

How do I use Mocha/Chai to start checking for DOM elements after a JavaScript Object create it?

Would I need to use a setTimeout? (And it works when I do). The problem I'm having is that the FeedObject does an asynchronous call, so when a new instance of it is created, it takes sometime before it can inject itself to the DOM.
describe('Default Case', function () {
before(function () {
$divHolder = $('#divHolder');
$divHolder.html('');
var myComponent = new FeedObject({
div: $divHolder[0],
prop: {
id: 'myFeed',
},
client: {}
});
myComponent.createDOMElements();
});
it('should exist', function () {
console.log($divHolder.find('.feed-item')); // This does not exist unless I use a timeout
});
EDIT: Just to clarify, the new FeedObject does an AJAX call.
What you need to determine when new FeedObject is done. Depending on what FeedObject provides, it could mean:
Providing a callback that it called when new FeedObject is done. Call done in the callback.
Listening to an event (most likely a custom jQuery event). Call done in the event handler.
Make the before hook return a promise that exists on the object returned by new FeedObject. For instance, the ready field could contain this promise, and you'd do return myComponent.ready. Mocha will wait for the promise to be resolved. (Remove the done argument from your before hook.)
I strongly suggest that you add one of the facilities above rather than use setTimeout. The problem with setTimeout is that it is always suboptimal: either you wait longer than you need for the DOM to be ready, or you set a timeout which is super close to the real time it take to be ready but in some cases the timeout will be too short (e.g. if your machine happens to have a spike of activity that does not leave enough CPU for the browser running the test code) and the test will fail for bogus reasons.
Well setTimeOut will work (until the server response takes longer than the time you specified and then it won’t work) but it’s definitely not the best or fastest way to do it. When you’re using an async call, you need to define a callback method that runs after the code is finished. The idea of the code should be something like this:
describe('Default Case', function () {
// Your code goes here
$.ajax = function(ajaxOpts)
{
var doneCallback = ajaxOpts.done;
doneCallback(simulatedAjaxResponse);
};
function fetchCallback(user)
{
expect(user.fullName).to.equal("Tomas Jakobsen");
done();
};
fetchCurrentUser(fetchCallback);
});
If you need even more details you could check this very helpful link. Hope that helps!
I would use this async chai plugin. If you are using a promise api (probably the most straight-forward way to handle async testing), this makes testing async ops very simple. You just have to remember to return any invocation that is async (or returns a promise) so chai knows to 'wait' before continuing.
describe(() => {
let sut
beforeEach(() => {
sut = new FeedObject()
return sut.createDomElements() // <- a promise
})
it('should exist', () => {
$('#target').find('.feed-item').should.exist
})
})
Also consider the goals of this testing: why are you doing it? I find that a lot of DOM insert/remove/exists testing is wasted effort. This is particularly true if you are testing a 3rd party library/framework as part of your application code. The burden of proof is on the library/framework to prove it is correct (and most well-written libraries already have a testing suite), not your app. If you are concerned about testing 'did I correctly invoke this 3rd party code' there are better ways to do that without touching the DOM.

Extending WinJS Promise object

I wanted an easy way to add pauses into some stuff that I chain together with promises.
I thought it would be nice to include a "wait" method so I could write stuff like:
var promise = new WinJS.Promise(function(complete){
whatever()
}).wait(1000).then(function(){
whatever2();
}).wait(500).done(function(){
alldone();
}
So to do this I added a wait method to the Promise class like so:
if (WinJS.Promise.prototype.wait == null) {
WinJS.Promise.prototype.wait = function (milliseconds) {
var promise = new WinJS.Promise(function (complete) {
setTimeout(complete, milliseconds);
});
return promise;
}
}
It seemed to be working, but I noticed that if I use a "then", the object I get back from it, while the documentation says is a WinJS.Promise, won't have a wait function. The promises I create all DO have the wait function, but calling .then() on a promise will cause the subsequent .wait to fail, so...
promise.wait(300).then().done();
is no problem but:
promise.then().wait(300).done();
will error out saying that there is no wait method on the Promise returned from then().
Can anyone explain what I'm doing wrong?
There are two reasons why your code doesn't work.
The first is that Microsoft has used their own approach to creating object instances, which they do through the WinJS.Class namespace - this means that the prototype you are altering with the addition of your wait function doesn't ever get applied to the WinJS.Promise objects you are consuming in your code.
The second problem is that you are targeting the wrong object - the WinJS.Promise.then method calls WinJS.Promise.as which returns a CompletePromise object - so even if you could make your wait function stick, it would be in the wrong place. The CompletePromise definition is not in the public scope, so you'd have to do a lot of hacking to be able make the change you want.
There is a solution, but you have to use the WinJS.Promise.timeout method. You can't use this inline, which means that to get the effect you want, you will need some slightly awkward code, as follows;
var promise = new WinJS.Promise(function (complete) {
whatever();
complete();
}).then(function () {
return WinJS.Promise.timeout(1000);
}).then(whatever2).then(function() {
return WinJS.Promise.timeout(500);
}).then(alldone);
This not a direct answer to your question. Adding a wait() method to Promise's prototype should indeed work, unless then() returns an object that looks like a Promise, quacks like a Promise, but is not actually a Promise.
That said, you do not have to implement a wait() method in the first place, because Promise already exposes a timeout() method that does the same thing (and more, actually). You're looking for its single-argument form:
var promise = new WinJS.Promise(function(complete) {
whatever();
}).timeout(1000).then(function() {
whatever2();
}).timeout(500).done(function() {
alldone();
});

Categories

Resources