I have a webservice which can take certain parameters, like such, ?top=5&orderby=column --- etc...
I want to be able to execute my object like such:
var call = new API();
call.get().top(5).skip(15).orderby("address");
The challenge is to only have the last orderby trigger the execute() method. Here is my code. Please let me know if you have any better ideas! It's currently delaying by 25ms when each function ends, and stops the timer when the next function starts. Is this proper/acceptable?
var API = function (webservice) {
this.webservice(webservice);
return this;
};
API.prototype = {
version: function (urlFormat) {
if (urlFormat) {
return "v" + urlFormat.split('.').join('_');
}
return sessionStorage.getItem("version");
},
path: function () {
return "../WebAPI/";
},
execute: function () {
var path = this.path() + this.webservice() + ".svc/";
if (this.__parameters) {
path += "?";
}
var first = true;
for (var k in this.__parameters) {
if (k !== "type")
path += ((first) ? (function(){first = false; return ""})() : "&") + "$" + k + "=" + this.__parameters[k];
};
console.log(this.__parameters.type + ": " + path);
return this;
},
put: function () {
this.doIt("type","put");
return this;
},
post: function () {
this.doIt("type","post");
return this;
},
get: function() {
this.doIt("type","get");
return this;
},
delete: function() {
this.doIt("type","delete");
return this;
},
toString: function () {
return "API";
},
webservice: function(webservice) {
if (webservice) {
this.__webservice = webservice;
}
else {
return this.__webservice;
}
},
top: function (p) {
this.doIt("top",p);
return this;
},
view: function (p) {
this.doIt("view",p);
return this;
},
orderby: function (p) {
this.doIt("orderby",p);
return this;
},
criteria: function (p) {
this.doIt("criteria",p);
return this;
},
skip: function (p) {
this.doIt("skip",p);
return this;
},
filter: function (p) {
this.doIt("filter",p);
return this;
},
doIt: function (method, parameter) {
this.__timerStop();
this.__parameters[method] = parameter;
this.__timerStart();
},
__timerStop: function () {
if (this.__timer) {
clearTimeout(this.__timer);
}
},
__timerStart: function (append) {
var self = this;
if (this.__timer) {
this.__timerStop();
}
this.__timer = setTimeout(function() {
console.log("executing.");
console.log(JSON.stringify(self.__parameters));
self.execute();
}, 25);
},
__parameters: {}
};
Update: You know what? I'm going to soften my stance on this one (original answer below). You should actually be OK given that the callback you're passing to setTimeout can never fire before your method chain is "complete" given JavaScript's single-threaded event loop. (And in fact, this also implies you should be safe passing 0 to setTimeout instead of 25.)
I still think you're nuts for thinking up this design (and if this is code that multiple developers will be touching, I'd say you're better off with a simpler design just to lessen the risk of team confusion from undue complexity); but if you insist on taking this path, you actually shouldn't run into any weird Heisenbugs.
But yeah, I stand by my original advice about requiring the execute call explicitly.
Oh man. You are crazy to even be considering this! I mean, part of me does love you for it (I am a big fan of horrifying hacks); but the fact is that taking this approach, while it might end up working, will drive you nuts if/when it goes haywire.
The main reason I would strongly discourage it is that the alternative is very easy and, more importantly, actually reliable: just establish the rule that execute is the method that actually sends the request, and so any chained method call must end with that:
call.get().top(5).skip(15).orderby("address").execute();
If you're seriously in love with this timer-based idea, something tells me you've never really suffered from a Heisenbug before (or, as I originally guessed, you're just out of your mind).
Interesting idea. Although, why not do something like this instead:
call({ type: "get", top: 5, skip: 15, orderby: "address" });
Then process each argument by looping through the object inside your call implementation, then make the service request.
for(var arg in args) {
if(args.hasOwnProperty(arg) && args.propertyIsEnumerable(arg)) {
// process argument
}
}
This keeps things simple.
Related
I have the following self-invoked function which is being utilized by the other functions across the app:
var Api = (function() {
var requestPayload;
var responsePayload;
return {
getRequestPayload: function() {
return requestPayload;
},
setRequestPayload: function(newPayloadStr) {
requestPayload = JSON.parse(newPayloadStr);
},
getResponsePayload: function() { // <-Function's output I need
return responsePayload;
},
setResponsePayload: function(newPayloadStr) {
responsePayload = JSON.parse(newPayloadStr);
}
};
}());
This function is called by other functions in the app like:
Api.getResponsePayload();
I want to capture the output of getResponsePayload function every time this function is called to utilize is for further processing.
I tried to create another function:
function runMe(responsePayload) {
console.log(responsePayload)
}
And it gets called everytime getResponsePayload function is called but the output I'm getting is undefined:
getResponsePayload: function() {
runMe();
return responsePayload;
How can I get the output of getResponsePayload function everytime it gets called by any other function in the app?
Just shim it, assuming this is for debug purposes. You may want to handle exceptions more explicitly, just be sure to rethrow to be transparent.
Api.getResponsePayload = (function(previousFn){
return function() {
var result = previousFn.apply(this, arguments);
// print them or something
return result;
}
})(Api.getResponsePayload)
Edit: Here is a generalisation of the method above:
function logMethodCallsOn(object, methodName) {
var actualMethod = object[methodName];
object[methodName] = function() {
var title = methodName + "(" + Array.prototype.map.call(arguments, function (val) { return JSON.stringify(val); }).join(", ") + ")";
try {
var result = actualMethod.apply(this, arguments);
console.log(title + " =", result);
return result;
} catch (e) {
console.error(title + " threw", e);
throw(e);
}
}
}
logMethodCallsOn(Api, 'getResponsePayload');
logMethodCallsOn(Api, 'setResponsePayload');
You could use callbacks. I'm not sure if you whant a callback in the get or the set, this example shows both.
Eric's answer is more elegant and can be used even if Api is an external object. But I think this simpler implementation could be useful for learning purpose.
var Api = (function(getResponsePayloadCallback, setResponsePayloadCallback) {
var requestPayload;
var responsePayload;
return {
getRequestPayload: function() {
return requestPayload;
},
setRequestPayload: function(newPayloadStr) {
requestPayload = JSON.parse(newPayloadStr);
},
getResponsePayload: function() { // <-Function's output I need
getResponsePayloadCallback(responsePayload);
return responsePayload;
},
setResponsePayload: function(newPayloadStr) {
responsePayload = JSON.parse(newPayloadStr);
setResponsePayloadCallback(responsePayload);
}
};
})(getResponsePayloadHandler, setResponsePayloadHandler);
Api.setResponsePayload('{ "foo": "foo value" }');
var requestPayload = Api.getResponsePayload();
function getResponsePayloadHandler(value) {
console.log("getResponsePayload: " + value.foo);
}
function setResponsePayloadHandler(value) {
console.log("setResponsePayload: " + value.foo);
}
You are not passing any parameter to runMe so it logs undefined. Change your getResponsePayload function to:
getResponsePayload: function() {
runMe(responsePayload);
return responsePayload;
}
Anyway be aware that if you don't set responsePayload, you console output will still be undefined cause responsePayload is only declared but never assigned a value (so it will have undefined value)
Why do I see higher-order functions being used in a lot of code? It seems like to me it is almost the same exact thing as a regular function only that with a normal function you can just return what you need, just like what returning a 2nd function does, it ends up returning a value in the end anyways but now you just had to write more code.
I've read this guide (source: link) and here the guy went from this:
function getAttribute(attr) {
return typeof this.getAttribute(attr) != 'undefined';
}
var accessors = {
sortable: {
get: function() {
return getAttribute('sortable');
}
},
droppable: {
get: function() {
return getAttribute('droppable');
}
}
};
to this:
function generateGetMethod(attr) {
return function() {
return typeof this.getAttribute(attr) != 'undefined';
};
}
var accessors = {
sortable: {
get: generateGetMethod('sortable')
},
droppable: {
get: generateGetMethod('droppable')
}
};
A quote from that link above states in the end: "This is a really useful technique that saves you from repeating likewise code and, when used correctly, is easy to understand and maintain!"
To me it confuses me because why is the function returning another function when I can either just assign the returning function into a separate variable, or just use a normal function with an extra argument?
Here is a normal function:
function water(userArg) {
return {
water: userArg
};
}
console.log(water('is liquid')); // returns: { water: 'is liquid' }
Here is a higher order function:
function water() {
return function (valueOfWater) {
return {
water: valueOfWater
};
};
}
console.log(water()('is liquid')); // returns: { water: 'is liquid' }
They both return the same thing. What powerful concept am I missing?
When assigning a normal function to a variable,
function getAttribute(attr) {
return typeof this.getAttribute(attr) != 'undefined';
}
var x = getAttribute(attr);
then x is going to be the value that the function returns. Nothing special here.
However, the power of higher-order functions is evident here:
function generateGetMethod(attr) {
return function() {
return typeof this.getAttribute(attr) != 'undefined';
};
}
var y = generateGetMethod(attr);
In this case, y is actually equal to a function, and can be called with different parameters, such as y(attributeA); and so on. In other words, the higher-order function in this case acts sort of like a generator, or factory method. It creates general functions that can be applied.
I have following function:
_processSelectedPlantWcOb: function (oSelectOb, oSetNewDates, oWpServiceOb, fnQueryDatesPlantWcOb) {
let self = this;
return oSelectOb
.map(function (oEvent) {
return oEvent.getSource();
})
.switchMap(function (oSelect) {
return oSetNewDates.mapTo(oSelect);
})
.map(function (oSelect) {
let oItem = oSelect.getSelectedItem();
let aKeys = oItem.getKey().split("/");
return {sPlant: aKeys[0], sWc: aKeys[1]};
})
.switchMap(function (oSelected) {
return fnQueryDatesPlantWcOb(oWpServiceOb, oSelected.sPlant, oSelected.sWc);
});
},
as you can see the last parameter is expect a function and the implementation of the function fnQueryDatesPlantWcOb looks as follow:
_processQueryDatesPlantWcOb: function (oWpServiceOb, sPlant, sWc) {
return oWpServiceOb
.switchMap(function (oModel) {
let oPlantFilter = new sap.ui.model.Filter("Plant", sap.ui.model.FilterOperator.EQ, sPlant);
let oWcFilter = new sap.ui.model.Filter("WorkCenter", sap.ui.model.FilterOperator.EQ, sWc);
return Rx.Observable.create(function (subscriber) {
oModel.read("/CostCenterCalendarSet", {
success: function (oData, oResponse) {
subscriber.next(oResponse);
},
error: function (oError) {
subscriber.error(oError);
},
filters: [oPlantFilter, oWcFilter]
});
});
})
.filter(function (oData) {
return oData.data.results.length > 0
})
.mergeMap(function (oData) {
return Rx.Observable.from(oData.data.results);
})
.map(function (oData) {
let oDate = oData.InspectionDate;
return new Date(oDate.getFullYear(), oDate.getMonth(), oDate.getDate());
})
.filter(function (oDate) {
let oToday = new Date();
return oDate.getTime() > oToday.getTime();
})
.map(function (oDate) {
return oDate.getTime();
});
},
As you can see, the parameters sPlant and sWc will be use in the switchMap function.
What I want to know is, do I break the functional paradigm?
In my opinion, I do not break it, because every time when I pass the same sPlant and sWc values, I will get the same result, but I am not sure.
I don't program in JS, but the "functional paradigm" actually defines two properties regarding (pure) functions:
It always returns the same result if given the same arguments. This is called referential transparency, and you can add it to your list of $5 programming terms.
It can’t cause any side effects. That is, the function can’t make any changes that are observable outside the function itself—for example, by changing an externally accessible mutable object or writing to a file.
Since oModel.read("/CostCenterCalendarSet", ...) seems to do I/O, it's not a pure function (check out this question to see why reading is a side effect, too).
Furthermore, it's a controversial topic if functions using date/time can be referentially transparent at all—especially if it's not part of the parameter list.
So, I'm writing a web app. Pretty much everything is done client-side, the server is but a RESTful interface. I'm using jQuery as my framework of choice and implementing my code in a Revealing Module Pattern.
The wireframe of my code basically looks like this:
(function($){
$.fn.myplugin = function(method)
{
if (mp[method])
{
return mp[method].apply(this, Array.prototype.slice.call(arguments, 1));
}
else if (typeof method === 'object' || ! method)
{
return mp.init.apply(this, arguments);
}
else
{
$.error('Method ' + method + ' does not exist on $.myplugin');
}
};
var mp =
{
init : function( options )
{
return this.each(function()
{
// stuff
}
},
callbacks : {},
addCallback : function(hook_name, cb_func, priority)
{
// some sanity checking, then push cb_func onto a stack in mp.callbacks[hook_name]
},
doCallbacks : function(hook_name)
{
if (!hook_name) { hook_name = arguments.callee.caller.name; }
// check if any callbacks have been registered for hook_name, if so, execute one after the other
}
};
})(jQuery);
Pretty straightforward, right?
Now, we're able to register (multiple, hierarchical) callbacks from inside as well as from outside the application scope.
What is bugging me: To make the whole thing as extensible as possible, I'd have to resort to something along these lines:
foo : function() {
mp.doCallbacks('foo_before');
// do actual stuff, maybe some hookpoints in between
mp.doCallbacks('foo_after');
}
Every single function inside my app would have to start and end like that. This just doesn't seem right.
So, JS wizards of SO - what do?
You can write a function that takes another function as an argument, and returns a new function that calls your hooks around that argument. For instance:
function withCallbacks(name, func)
{
return function() {
mp.doCallbacks(name + "_before");
func();
mp.doCallbacks(name + "_after");
};
}
Then you can write something like:
foo: withCallbacks("foo", function() {
// Do actual stuff, maybe some hookpoints in between.
})
I might have not understood the question correctly, because I don't see why you don't add the code to call the callbacks directly in the myplugin code:
$.fn.myplugin = function(method)
{
if (mp[method])
{
var params = Array.prototype.slice.call(arguments, 1), ret;
// you might want the callbacks to receive all the parameters
mp['doCallbacks'].apply(this, method + '_before', params);
ret = mp[method].apply(this, params);
mp['doCallbacks'].apply(this, method + '_after', params);
return ret;
}
// ...
}
EDIT:
Ok, after reading your comment I think another solution would be (of course) another indirection. That is, have an invoke function that's being used from the constructor as well as the other public methods for calls between themselves. I would like to point out that it will only work for the public methods, as attaching to private methods' hooks breaks encapsulation anyway.
The simple version would be something like this:
function invoke(method) {
var params = Array.prototype.slice.call(arguments, 1), ret;
// you might want the callbacks to receive all the parameters
mp['doCallbacks'].apply(this, method + '_before', params);
ret = mp[method].apply(this, params);
mp['doCallbacks'].apply(this, method + '_after', params);
}
$.fn.myplugin = function() {
// ...
invoke('init');
// ...
};
But, I've actually written a bit more code, that would reduce the duplication between plugins as well.
This is how creating a plugin would look in the end
(function() {
function getResource() {
return {lang: "JS"};
}
var mp = NS.plugin.interface({
foo: function() {
getResource(); // calls "private" method
},
bar: function() {
this.invoke('foo'); // calls "fellow" method
},
init: function() {
// construct
}
});
$.fn.myplugin = NS.plugin.create(mp);
})();
And this is how the partial implementation looks like:
NS = {};
NS.plugin = {};
NS.plugin.create = function(ctx) {
return function(method) {
if (typeof method == "string") {
arguments = Array.prototype.slice.call(arguments, 1);
} else {
method = 'init'; // also gives hooks for init
}
return ctx.invoke.apply(ctx, method, arguments);
};
};
// interface is a reserved keyword in strict, but it's descriptive for the use case
NS.plugin.interface = function(o) {
return merge({
invoke: NS.plugin.invoke,
callbacks: {},
addCallback: function(hook_name, fn, priority) {},
doCallbacks: function() {}
}, o);
};
NS.plugin.invoke = function(method_name) {
if (method_name == 'invoke') {
return;
}
// bonus (if this helps you somehow)
if (! this[method]) {
if (! this['method_missing') {
throw "Method " + method + " does not exist.";
} else {
method = 'method_missing';
}
}
arguments = Array.prototype.slice.call(arguments, 1);
if (method_name in ["addCallbacks", "doCallbacks"]) {
return this[method_name].apply(this, arguments);
}
this.doCallbacks.apply(this, method_name + '_before', arguments);
var ret = this[method_name].apply(this, arguments);
this.doCallbacks.apply(this, method_name + '_after', arguments);
return ret;
};
Of course, this is completely untested :)
you are essentially implementing a stripped-down version of the jQueryUI Widget factory. i'd recommend using that functionality to avoid having to roll this yourself.
the widget factory will auto-magically map strings to method calls, such that:
$("#foo").myPlugin("myMethod", "someParam")
will call myMethod on the plugin instance with 'someParam' as an argument. Additionally, if you fire a custom event, users can add callbacks by adding an property to the options that matches the event name.
For example, the tabs widget has a select event that you can tap into by adding a select property to the options during initialization:
$("#container").tabs({
select: function() {
// This gets called when the `select` event fires
}
});
of course, you'll need to add the before and after hooks as events to be able to borrow this functionality, but that often leads to easier maintenance anyhow.
hope that helps. cheers!
Basically I prefer to avoid callbacks and use events instead. The reason is simle. I can add more than one functions to listen given event, I don't have to mess with callback parameters and I don't have to check if a callback is defined. As far as all of your methods are called via $.fn.myplugin it's easy to trigger events before and after method call.
Here is an example code:
(function($){
$.fn.myplugin = function(method)
{
if (mp[method])
{
$(this).trigger("before_"+method);
var res = mp[method].apply(this, Array.prototype.slice.call(arguments, 1));
$(this).trigger("after_"+method);
return res;
}
else if (typeof method === 'object' || ! method)
{
return mp.init.apply(this, arguments);
}
else
{
$.error('Method ' + method + ' does not exist on $.myplugin');
}
};
var mp =
{
init : function( options )
{
$(this).bind(options.bind);
return this.each(function()
{
// stuff
});
},
foo: function() {
console.log("foo called");
}
};
})(jQuery);
$("#foo").myplugin({
bind: {
before_foo: function() {
console.log("before foo");
},
after_foo: function() {
console.log("after foo");
}
}
});
$("#foo").myplugin("foo");
Sorry if my question wasn't clear enough. I'll put my code here...
var chain = {
'fn_1' : {
//fn_1 code here
chain.fn_2();},
'fn_2' : {
//fn_2 code here
chain.fn_3();}
...and so on
}
Let's say if i wana call chain.fn_1(), is there a way I can do that without calling chain.fn_2()?
What I can think of right now is a flag, but that would be alot of excess flags probably for each function. Do you guys have any ideas?
If the series of functions each call the next one you're correct, you'd need to have some sort of flag. In all likelihood, what would be best would be to modify your functions so that they return the reference to the object. Then you could chain like so:
var chain = {
'fn_1': function () {
// do something here.
return this;
},
'fn_2': function () {
// do something here.
return this;
},
'fn_3': function () {
// do something here.
return this;
}
};
// call the full chain:
chain.fn_1().fn_2().fn_3();
// call only the middle.
chain.fn_2();
g.d.d.c's answer is best, but if you can't modify the object for some reason, you could do this:
var _oldFn2 = chain.fn_2
chain.fn_2 = function() { return; };
chain.fn_1();
chain.fn_2 = _oldFn2;
var chain = {
fn : ['fn1', 'fn2', 'fn3'],
call : function(name) {
var i = 0, pos = -1, l = this.fn.length;
for(i = 0; i < l; i += 1) {
if(this.fn[i] == name) {
pos = i;
}
if(pos !== -1) {
this[this.fn[i]]();
}
}
},
fn1 : function() {
alert('fn1');
},
fn2 : function() {
alert('fn2');
},
};
chain.call('fn1'); //chain
chain.fn1(); //single