I think I'm trying to call a Promise within a Promise (maybe even within another Promise). In the past I try to simplify my question and I end up getting more questions, so there's a lot below:
I have the following code as a module named myModule:
let https = require('https');
module.exports.getApiOne = function(value) {
var params = {..., path = '/api/getOne/' + value, ...};
return getApi(params).then(response => response);
};
module.exports.getApiTwo = function(value) {
var params = {..., path = '/api/getTwo/' + value, ...};
return getApi(params).then(response => response);
};
function getApi(params) {
return new Promise(function(resolve, reject) {
var req = https.request(params, function(res) {
var body = [];
res.on('data', function(chunk) {
body.push(chunk);
});
res.on('end', function() {
try {
body = Buffer.concat(body).toString();
} catch (e) {
reject(e);
}
resolve(body);
});
});
req.on('error', function(err) {
reject(err);
});
req.end();
});
}
On another file I have:
const status = require('myModule');
var someObject = {};
function someFunction(inputObject) {
// initialize object
if (!someObject[inputObject.Id]) {
someObject[inputObject.Id] = {};
someObject[inputObject.Id].contact = {};
}
// get object
var objectForThis = someObject[inputObject.Id];
switch (inputObject.someVal) {
case 'test':
//... some code
objectForThis.stage = 'test';
break;
case 'hello':
status.getApiOne('world').then(response => {
console.log(response);
objectForThis.stage = 'zero'
});
break;
default:
someOtherFunction(objectForThis.stage).then(response => {
objectForThis.stage = response;
});
break;
}
someObject[inputObject.Id] = objectForThis;
}
function someOtherFunction(stage) {
var newStage;
new Promise(function(resolve, reject) {
switch (stage) {
case 'zero':
// some code
newStage = 'one';
case 'one':
status.getApiTwo('foo').then(response => {
console.log(response);
newStage = 'two';
/********************************************
I assume, the problem lies here, it's
'resolving' (below) before this sets the new
value for 'newStage'
********************************************/
});
break;
default:
// do nothing
break;
}
});
resolve(newStage);
}
When I call
someFunction({id = 1, someValue = 'test'}); // sets 'stage' to 'test'
someFunction({id = 1, someValue = 'hello'}); // sets 'stage' to 'zero'
someFunction({id = 1, someValue = 'foo'}); // sets 'stage' to 'one'
someFunction({id = 1, someValue = 'bar'}); // NOT setting 'stage' to 'two'
The reason for why is because Promises are asynchronous:
logOut("start of file");
new Promise(function(accept){
accept();
}).then(function(){
logOut("inside promise");
});
function makePromise(name) {
new Promise(function(accept){
accept();
}).then(function(){
logOut("inside promise inside makePromise " + name);
});
};
logOut("value returned from makePromise: " + makePromise("one"));
try {
// just to prove this
makePromise("two").then(function(accept){
accept();
}).then(function(){
logOut("after makePromise");
});
} catch(err) {
logOut("Failed to `.then` the return value from makePromise because:\n" + err.message);
}
logOut("end of file");
var outputList;
function logOut(str){
outputList = outputList || document.getElementById("output");
outputList.insertAdjacentHTML("beforeend", "<li><pre>" + str + "</pre></li>");
}
<ol id="output"></ol>
As seen above, the whole program does not pause for the .then statement. That is why they are called Promises: because the rest of the code goes on while the Promise is waiting to be resolved. Further, as seen above, there can be a value returned from a function only if the function explicitly returns the value via the then keyword. JavaScript functions do not automatically return the value of the last executed statement.
Please see my website here for more info on Promises.
In the demo below, I have attempted to fix up the fragments of the files you slapped on this question. Then, I proceeded to wrap them together into a quick single-file system I typed up
(function(){"use strict";
// NOTE: This setup code makes no attempt to accurately replicate the
// NodeJS api. This setup code only tries to concisely mimics
// the NodeJS API solely for the purposes of preserving your code
// in its present NodeJS form.
var modules = {}, require = function(fileName){return modules[fileName]};
for (var i=0; i < arguments.length; i=i+1|0)
arguments[i]({exports: modules[arguments[i].name] = {}}, require);
})(function https(module, require){"use strict";
////////////// https.js //////////////
module.exports.request = function(options, withWrapper) {
var p, when = {}, wrapper = {on: function(name, handle){
when[name] = handle;
}, end: setTimeout.bind(null, function(){
if (p === "/api/getOne/world") when.data("HTTP bar in one 1");
else if (p === "/api/getTwo/foo") when.data("HTTP foo in two 2");
else {console.error("Not stored path: '" + p + "'");
return setTimeout(when.error);}
setTimeout(when.end); // setTimeout used for asynchrony
}, 10 + Math.random()*30)}; // simulate random http delay
setTimeout(withWrapper, 0, wrapper); // almost setImmediate
return p = options.path, options = null, wrapper;
};
/******* IGNORE ALL CODE ABOVE THIS LINE *******/
}, function myModule(module, require) {"use strict";
////////////// myModule.js //////////////
const HttpsModule = require('https');
module.exports.getApiOne = function(value) {
var params = {path: '/api/getOne/' + value};
// There is no reason for `.then(response => response);`!
// It does absolutely nothing.
return getApi(params); // .then(response => response);
};
module.exports.getApiTwo = function(value) {
var params = {path: '/api/getTwo/' + value};
return getApi(params); // .then(response => response);
};
function getApi(params) {
return new Promise(function(resolve, reject) {
var req = HttpsModule.request(params, function(res) {
var body = [];
res.on('data', function(chunk) {
body.push(chunk);
});
res.on('end', function() {
try {
body = body.join("");//Buffer.concat(body).toString();
} catch (e) {
reject(e);
}
resolve(body);
});
});
req.on('error', function(err) {
reject(err);
});
req.end();
});
}
}, function main(module, require) {"use strict";
////////////// top JS script //////////////
const MyStatusModule = require('myModule');
const isPromise = isPrototypeOf.bind(Promise.prototype)
var someObject = {};
function someFunction(inputObject) {
// initialize object
// NOTE: Javascript IS case-sensitive, so `.Id` !== `.id`
if (!someObject.hasOwnProperty(inputObject.id)) {
someObject[inputObject.id] = {};
someObject[inputObject.id].contact = {};
}
// get object
var objectForThis = someObject[inputObject.id];
switch (inputObject.someValue) {
case 'test':
//... some code
return objectForThis.stage = 'test';
break;
case 'hello':
return MyStatusModule.getApiOne('world').then(function (response) {
// console.log(response);
return objectForThis.stage = 'zero'
});
break;
default:
return someOtherFunction(objectForThis.stage).then(function (response) {
return objectForThis.stage = response;
});
break;
}
}
function someOtherFunction(stage) {
var newStage;
// If you return nothing, then you would be calling `.then` on
// on `undefined` (`undefined` is the default return value).
// This would throw an error.
return new Promise(function(resolve, reject) {
switch (stage) {
case 'zero':
// some code
newStage = 'one';
resolve(newStage); // you must call `resolve`
case 'one':
return MyStatusModule.getApiTwo('foo').then(function (response) {
// console.log(response);
newStage = 'two';
/********************************************
I assume, the problem lies here, it's
'resolving' (below) before this sets the new
value for 'newStage'
********************************************/
resolve(newStage); // you must call `resolve`
});
break;
default:
// do nothing
resolve(newStage); // you must call `resolve`
break;
}
});
}
// tests:
function logPromise(){
var a=arguments, input = a[a.length-1|0];
if (isPromise(input)) {
for (var c=[null], i=0; i<(a.length-1|0); i=i+1|0) c.push(a[i]);
return input.then(logPromise.bind.apply(logPromise, c));
} else console.log.apply(console, arguments);
}
logPromise("test->test: ", someFunction({id: 1, someValue: 'test'})); // sets 'stage' to 'test'
logPromise("hello->zero: ", someFunction({id: 1, someValue: 'hello'})) // sets 'stage' to 'zero'
.finally(function(){ // `.finally` is like `.then` without arguments
// This `.finally` waits until the HTTP request is done
logPromise("foo->one: ", someFunction({id: 1, someValue: 'foo'})) // sets 'stage' to 'one'
.finally(function(){
debugger;
logPromise("bar->two: ", someFunction({id: 1, someValue: 'bar'})); // NOT setting 'stage' to 'two'
});
});
});
If it is not apparent already, do not copy the above snippet into your code. It will break your code because the above snippet is rigged with dummy Node modules designed to produce set results. Instead, copy each individual file (each wrapped in a function) from the snippet above into the corresponding file of your code if you must copy. Also, while copying, keep in mind not to copy the dummy stuff above the blatant IGNORE ALL CODE ABOVE THIS LINE indicator. Also, do not forget to test rigorously. I am much more familiar with browser JavaScript than Node JavaScript, so it is possible (though unlikely) that I may have introduced a potential source of errors.
someObject[inputObject.id] = objectForThis; is not needed
I could give you a super concise quick answer to this question. However, I feel that the "quick" answer would not do you justice because this particular question requires a far greater depth of understanding to be able come up with an explanation than to simply read someone else's explanation. Further, this is a very crucial concept in Javascript. Thus, it is very necessary to be able to come up with the explanation on your own so that you do not run into the same trouble again 5 minutes from now. Thus I have written the below tutorial to guide you to the answer so that you can be sure that you have complete understanding.
In Javascript, there are expressions such as 7 - 4 which yields 3. Each expression returns a value that may be used by further expressions. 3 * (4 + 1) first evaluates 4 + 1 into 3 * 5, then that yields 15. Assignment expressions (=,+=,-=,*=,/=,%=,**=,&=,|=, and ^=) only change the left-hand variable. Any right-hand variable stays the exact same and contains the same value:
var p = {};
var ptr = p;
// Checkpoint A:
console.log('Checkpoint A: p === ptr is now ' + (p === ptr));
ptr = null;
// Checkpoint B:
console.log('Checkpoint B: p === ptr is now ' + (p === ptr));
Let us examine what p and ptr look like at Checkpoint A.
As seen in the above diagram, both p and ptr are kept separate even though they both point to the same object. Thus, at Checkpoint B, changing ptr to a different value does not change p to a different value. At Checkpoint B, variable p has remained unchanged while ptr is now null.
At Checkpoint A, keep in mind that (although p and ptr are distinct variables with different memory addresses) p and ptr both point to the same object because the location in memory they point to is the same index number. Thus, if we changed this object at checkpoint A, then reading/writing the properties of p would be the same as reading/writing the properties of ptr because you are changing the data being pointed to, not which variable is pointing to what data.
function visualize(inputObject) {
// displays an object as a human-readable JSON string
return JSON.stringify(inputObject);
}
var p = {};
p.hello = "world";
// Checkpoint 0:
console.log('Checkpoint 0: p is ' + visualize(p));
var ptr = p;
ptr.foo = "bar";
// Checkpoint A:
console.log('Checkpoint A: p is ' + visualize(p) + ' while ptr is ' + visualize(ptr));
ptr = null;
// Checkpoint B:
console.log('Checkpoint B: p is ' + visualize(p) + ' while ptr is ' + visualize(ptr));
p.foo = p.hello;
// Checkpoint C:
console.log('Checkpoint C: p is ' + visualize(p) + ' while ptr is ' + visualize(ptr));
As seen at Checkpoint A above, changing p is the same as changing ptr. What about when we reassign an object? The old object which is no longer pointed to get cleaned up by tan automatic Garbage Collector.
function visualize(inputObject) {
// displays an object as a human-readable JSON string
return JSON.stringify(inputObject);
}
var first = {one: "is first"};
first = {uno: "es el primero"};
var second = {two: "is second"};
second = first;
console.log("second is " + JSON.stringify(second));
Function arguments are the same as variables in this respect.
var obj = {};
setVariable(obj);
obj.key = "value";
console.log("obj is " + JSON.stringify(obj));
function setVariable(inputVariable){
inputVariable.setValue = "set variable value";
inputVariable = null;
}
Is the same as:
var obj = {};
/*function setVariable(*/ var inputVariable = obj; /*){*/
inputVariable.setValue = "set variable value";
inputVariable = null;
/*}*/
obj.key = "value";
console.log("obj is " + JSON.stringify(obj));
Objects are also no different:
function visualize(inputObject) {
// displays an object as a human-readable JSON string
return JSON.stringify(inputObject);
}
var variables = {};
var aliasVars = variables;
// Now, `variables` points to the same object as `aliasVars`
variables.p = {};
aliasVars.p.hello = "world";
// Checkpoint 0:
console.log('Checkpoint 0: variables are ' + visualize(variables));
console.log('Checkpoint 0: aliasVars are ' + visualize(aliasVars));
variables.ptr = aliasVars.p;
aliasVars.ptr.foo = "bar";
// Checkpoint A:
console.log('Checkpoint A: variables are ' + visualize(variables));
console.log('Checkpoint A: aliasVars are ' + visualize(aliasVars));
variables.ptr = null;
// Checkpoint B:
console.log('Checkpoint B: variables are ' + visualize(variables));
console.log('Checkpoint B: aliasVars are ' + visualize(aliasVars));
aliasVars.p.foo = variables.p.hello;
// Checkpoint C:
console.log('Checkpoint C: variables are ' + visualize(variables));
console.log('Checkpoint C: aliasVars are ' + visualize(aliasVars));
Next is object notation just to make sure that we are on the same page.
var obj = {};
obj.one = 1;
obj.two = 2;
console.log( "obj is " + JSON.stringify(obj) );
is the same as
var obj = {one: 1, two: 2};
console.log( "obj is " + JSON.stringify(obj) );
is the same as
console.log( "obj is " + JSON.stringify({one: 1, two: 2}) );
is the same as
console.log( "obj is {\"one\":1,\"two\":2}" );
The hasOwnProperty allows us to determine whether or not an object has a property.
var obj = {};
// Checkpoint A:
console.log("Checkpoint A: obj.hasOwnProperty(\"hello\") is " + obj.hasOwnProperty("hello"));
console.log("Checkpoint A: obj[\"hello\"] is " + obj["hello"]);
obj.hello = "world"; // now set the variable
// Checkpoint B:
console.log("Checkpoint B: obj.hasOwnProperty(\"hello\") is " + obj.hasOwnProperty("hello"));
console.log("Checkpoint B: obj[\"hello\"] is " + obj["hello"]);
obj.hello = undefined;
// Checkpoint C:
console.log("Checkpoint C: obj.hasOwnProperty(\"hello\") is " + obj.hasOwnProperty("hello"));
console.log("Checkpoint C: obj[\"hello\"] is " + obj["hello"]);
delete obj.hello;
// Checkpoint D:
console.log("Checkpoint D: obj.hasOwnProperty(\"hello\") is " + obj.hasOwnProperty("hello"));
console.log("Checkpoint D: obj[\"hello\"] is " + obj["hello"]);
Prototypes in javascript are crucial to understanding hasOwnProperty and work as follows: when a property is not found in an object, the object's __proto__ is checked for the object. When the object's __proto__ does not have the property, the object's __proto__'s __proto__ is checked for the property. Only after an object without a __proto__ is reached does the browser assume that the wanted property does not exist. The __proto__ is visualized below.
var obj = {};
console.log('A: obj.hasOwnProperty("foo") is ' + obj.hasOwnProperty("foo"));
console.log('A: obj.foo is ' + obj.foo);
obj.__proto__ = {
foo: 'value first'
};
console.log('B: obj.hasOwnProperty("foo") is ' + obj.hasOwnProperty("foo"));
console.log('B: obj.foo is ' + obj.foo);
obj.foo = 'value second';
console.log('C: obj.hasOwnProperty("foo") is ' + obj.hasOwnProperty("foo"));
console.log('C: obj.foo is ' + obj.foo);
delete obj.foo;
console.log('D: obj.hasOwnProperty("foo") is ' + obj.hasOwnProperty("foo"));
console.log('D: obj.foo is ' + obj.foo);
delete obj.__proto__.foo;
console.log('E: obj.hasOwnProperty("foo") is ' + obj.hasOwnProperty("foo"));
console.log('E: obj.foo is ' + obj.foo);
Infact, we could even store a reference to the __proto__ and share this reference between objects.
var dog = {noise: "barks"};
var cat = {noise: "meow"};
var mammal = {animal: true};
dog.__proto__ = mammal;
cat.__proto__ = mammal;
console.log("dog.noise is " + dog.noise);
console.log("dog.animal is " + dog.animal);
dog.wagsTail = true;
cat.clawsSofa = true;
mammal.domesticated = true;
console.log("dog.wagsTail is " + dog.wagsTail);
console.log("dog.clawsSofa is " + dog.clawsSofa);
console.log("dog.domesticated is " + dog.domesticated);
console.log("cat.wagsTail is " + cat.wagsTail);
console.log("cat.clawsSofa is " + cat.clawsSofa);
console.log("cat.domesticated is " + cat.domesticated);
However, the above syntax is terribly illperformant because it is bad to change the __proto__ after the object has been created. Thus, the solution is to set the __proto__ along with the creation of the object. This is called a constructor.
function Mammal(){}
// Notice how Mammal is a function, so you must do Mammal.prototype
Mammal.prototype.animal = true;
var dog = new Mammal();
// Notice how dog is an instance object of Mammal, so do NOT do dog.prototype
dog.noise = "bark";
var cat = new Mammal();
cat.noise = "meow";
console.log("dog.__proto__ is Mammal is " + (dog.__proto__===Mammal));
console.log("cat.__proto__ is Mammal is " + (cat.__proto__===Mammal));
console.log("dog.__proto__ is Mammal.prototype is " + (dog.__proto__===Mammal.prototype));
console.log("cat.__proto__ is Mammal.prototype is " + (cat.__proto__===Mammal.prototype));
console.log("dog.noise is " + dog.noise);
console.log("dog.animal is " + dog.animal);
dog.wagsTail = true;
cat.clawsSofa = true;
Mammal.prototype.domesticated = true;
console.log("dog.wagsTail is " + dog.wagsTail);
console.log("dog.clawsSofa is " + dog.clawsSofa);
console.log("dog.domesticated is " + dog.domesticated);
console.log("cat.wagsTail is " + cat.wagsTail);
console.log("cat.clawsSofa is " + cat.clawsSofa);
console.log("cat.domesticated is " + cat.domesticated);
Next, the this object in Javascript is not a reference to the instance like in Java. Rather, the this in Javascript refers to the object in the expression object.property() when functions are called this way. When functions are not called this way, the this object refers to undefined in strict mode or window in loose mode.
"use strict"; // "use strict"; at the VERY top of the file ensures strict mode
function logThis(title){
console.log(title + "`this === undefined` as " + (this === undefined));
if (this !== undefined) console.log(title + "`this.example_random_name` as " + this.example_random_name);
}
logThis.example_random_name = "log this raw function";
logThis("logThis() has ");
var wrapper = {logThis: logThis, example_random_name: "wrapper around logThis"};
wrapper.logThis("wrapper.logThis has ");
var outer = {wrapper: wrapper, example_random_name: "outer wrap arounde"};
outer.wrapper.logThis("outer.wrapper.logThis has ");
We can finally take all this knowledge and extrapolate it out to the real example.
var someObject = {};
function someFunction(inputObject) {
if (!someObject.hasOwnProperty(inputObject.id)) {
someObject[inputObject.id] = {};
someObject[inputObject.id].contact = {};
}
// get object
var objectForThis = someObject[inputObject.id];
objectForThis.stage = inputObject.stage;
}
var setTo = {};
setTo.id = 1;
setTo.stage = "first stage";
someFunction(setTo);
console.log("someObject is " + JSON.stringify(someObject));
First, let us inline the function and the setTo
var someObject = {};
var setTo = {id: 1, stage: "first stage"};
/*function someFunction(*/ var inputObject = setTo; /*) {*/
if (!someObject.hasOwnProperty(inputObject.id)) {
someObject[inputObject.id] = {};
someObject[inputObject.id].contact = {};
}
// get object
var objectForThis = someObject[inputObject.id];
objectForThis.stage = inputObject.stage;
/*}*/
console.log("someObject is " + JSON.stringify(someObject));
Next, let us inline the setTo object.
var someObject = {};
var setTo = {id: 1, stage: "first stage"};
if (!someObject.hasOwnProperty(setTo.id)) {
someObject[setTo.id] = {};
someObject[setTo.id].contact = {};
}
// get object
var objectForThis = someObject[setTo.id];
objectForThis.stage = setTo.stage;
console.log("someObject is " + JSON.stringify(someObject));
Then:
var someObject = {};
if (!someObject.hasOwnProperty(1)) {
someObject[1] = {};
someObject[1].contact = {};
}
// get object
var objectForThis = someObject[1];
objectForThis.stage = "first stage";
console.log("someObject is " + JSON.stringify(someObject));
To visually demonstrate the pointers:
var someObject = {};
if (!someObject.hasOwnProperty(1)) {
var createdObject = {};
someObject[1] = createdObject;
someObject[1].contact = {};
}
// get object
var objectForThis = someObject[1];
console.log("createdObject === objectForThis is " + (createdObject === objectForThis));
objectForThis.stage = "first stage";
console.log("someObject is " + JSON.stringify(someObject));
Please tell me if you have any further questions.
If you are just glancing at this, then please don't forget to read the full article above. I promise you that my article above is shorter and tries to go deeper into Javascript than any other article you may find elsewhere on the internet.
No, your someOtherFunction should not use new Promise. You should just chain onto the status.getApiTwo('foo') call, or create immediately resolved promises using Promise.resolve. That way it will always return a promise like your call in someFunction expects it.
function someOtherFunction(stage) {
switch (stage) {
case 'zero':
// some code
return Promise.resolve('one');
case 'one':
return status.getApiTwo('foo').then(response => {
// ^^^^^^
console.log(response);
return 'two';
// ^^^^^^
});
default:
// do nothing
return Promise.resolve(undefined);
}
}
Alternatively you could use async/await:
async function someOtherFunction(stage) {
switch (stage) {
case 'zero':
// some code
return 'one';
case 'one':
const response = await status.getApiTwo('foo');
console.log(response);
return 'two';
default:
// do nothing
break;
}
}
I'm trying to build a function in JS that has a return composed of different nested functions based on a parameter passed by the user.
function addA(otherFunction)
{
//gets the result from some base function and modifies it
//e.g. +1
}
function addB(otherFunction)
{
//does the same thing as addA, except different values. Consider it a variation of addA.
//eg. -1
}
function constr(input)
{
//based on the chars in input, we will recursively select a new function to be applied.
//the value of this function should be a function
if (...) return addA(constr(shorterInput))
if (*last char) return addA
if (*last char) return addB
if (...) return addB(constr(shorterInput))
}
So far, my script is recognizing addA and and addB as functions. But when it strings two functions together, for example
addB(addA)
The type becomes undefined. Can anybody let me know why it does not register as a function and/or the proper way to return nested functions. Thanks!
Edit: Here is the real code:
function cons(a,b)
{
return function (selector) {
return selector(a,b);
};
}
function a(list)
{
function aHelper(a,b)
{
return a
}
return list(aHelper);
}
function d(list)
{
function dHelper(a,b)
{
return b
}
return list(dHelper);
}
function abc(input)
{
if (input.length==0 || input==null) return null;
var x=input.charAt(input.length-1);
if (x==='a')
{
if (input.length>1)
{
var z=a(abc(input.substr(0,input.length-1)));
return z;
}
return a;
}
if (x==='d')
{
if (input.length>1)
{
var z=d(abc(input.substr(0,input.length-1)));
return z;
}
return d;
}
}
function show(list) {
var sval;
if (list == null) return '()';
else if (typeof list!='string')
{
sval = '(' + show(a(list)) + ' ' + show(d(list)) + ')';
}
else
{
sval=list;
}
return sval;
}
var func=abc('ad');
var func2=abc('a');
var list=cons('a',cons('b','c'));
console.log(typeof func);
console.log(typeof func2);
console.log(typeof list);
console.log(typeof func2(list));
console.log(typeof func(list));
Your function abc is supposed to return a function that can process lists, like a or d. However, you match that signature only in 2 out of 7 cases:
return a, return d are fine
return null - that's not a callable value
z = d(…); return z does return a list
z = a(…); return a does return an element of the list (of whatever type)
d(abc(…)) and a(abc(…)) use abc as if it would return a list
A correct implementation would look like this:
function abc(directions) {
if (directions.length == 0) {
return function id(list) { return list; }; // a function that does nothing
}
var f = directions[0] == 'a' ? car : cdr; // ignoring other values, you might also throw an error
var processRest = abc(input.slice(1));
return function(list) { // make a function to process a list
var z = f(list); // do the current operation
return processRest(z); // do the rest of operations
};
}
Or even better/shorter with the help of higher-order function composition:
function id(x) { return x; }
function compose(f, g) {
if (f == id) return g;
if (g == id) return f;
return function(x) { return f(g(x)); };
}
function abc(dirs) {
return !dirs.length ? id : compose(abc(dirs.slice(1)), dirs[0]=='a'?car:cdr);
}
function modifyFunction(f) {
return function () {
var returnValue = f.apply(this, arguments);
console.log(returnValue);
if (returnValue == undefined) {
return this;
} else {
return returnValue;
}
};
}
function modifyMethod(o, m) {
if (o.hasOwnProperty(m)) {
if (o[m] instanceof Function) {
o[m] = modifyFunction(m);
}
}
}
var o = {
num: 0,
add: function (x) {
return this.num += x;
},
sub: function (x) {
return this.num -= x;
}
};
modifyMethod(o, "add");
o.add(2).add(4);
console.log(o.num); // o.num = 6
modifyMethod(o, "sub");
o.sub(1).add(3).sub(5);
console.log(o.num); // o.num = 3
How would I make it so that in modifyMethod function inside of the "if(o[m] instanceof Function)" would be equal to what returns by the modifyFunction function, when sending it o[m]? I am trying to make it so that it is chainable, yet I am having a very difficult time getting my head around this.
To make o.add(2).add(4); equal to o.add(2); o.add(4); We can observe that what o.add(2) returns should be o. So your modifyFunction should return a function that :
call the passed in function with given parameters.
Return the caller.
So instead of return that returnValue(which is o.num, a number), you should always return this.
Another point is that in your modifyMethod, after you checked if o[m] is a function, you should pass in that function, not m which is just the key. So it should be o[m] = modifyFunction(o[m]);
function modifyFunction(f) {
return function () {
var returnValue = f.apply(this, arguments);
// Return the object that call this function,
// so it becomes chainable.
return this;
};
}
function modifyMethod(o, m) {
if (o.hasOwnProperty(m)) {
if (o[m] instanceof Function) {
// Pass o[m], not m, m is a string.
o[m] = modifyFunction(o[m]);
}
}
}
var o = {
num: 0,
add: function (x) {
return this.num += x;
},
sub: function (x) {
return this.num -= x;
}
};
modifyMethod(o, "add");
o.add(2).add(4);
console.log(o.num); // o.num = 6
modifyMethod(o, "sub");
o.sub(1).add(3).sub(5);
console.log(o.num); // o.num = 3
I'm looking to lower my overhead on code like this
foo(bar(baz("hello"))) // function hell
ideally something like this
var fbb = bind(foo, bar, baz)
foo("hello")
Does this exist? Native or library?
I looked through underscore and bind.
Underscore has the compose function which will do what you want:
var composite = _.compose(foo, bar, baz);
composite('hello');
function foo(a1){
return 'foo' + a1;
}
function bar(a2){
return 'bar' + a2;
}
function baz(a3){
return 'baz' + a3;
}
alert(foo(bar(baz("hello"))));
var composite = _.compose(foo, bar, baz);
alert( composite('hello') );
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.7.0/underscore-min.js"></script>
function getCaller(first) {
var rest = Array.prototype.slice.call(arguments, 1);
return function (value) {
return rest.reduce(function (previous, next) {
return next(previous);
}, first(value));
};
}
function foo(string) {
return string + ' world!';
}
function bar(string) {
return string + ' Hi';
}
function baz(string) {
return string + ' Mom!';
}
var caller = getCaller(foo, bar, baz);
console.log(caller('Hello'));
// Prints: Hello world! Hi Mom!
var bind = function() {
var fns = Array.prototype.slice.call(arguments).reverse();
return function(value) {
for (var key in fns) {
var fn = fns[key];
console.log(fn);
value = fn(value);
}
return value;
}
}
function plusTomato(value) {
return value + "tomato";
}
function plusPear(value) {
return value + "pear";
}
var plus = bind(plusTomato, plusPear);
var y = plus("pancake"); //pankaketomatopear
console.log(y);
var x = plusTomato(plusPear("pancake")); //pankaketomatopear
console.log(x);
I'm keeping a record of each time various functions are called. I have a function called
record_activity( function_name );
I really don't want to have to write this at the top of every function I want to track. Currently there are lots of functions in the format:
Object.Key.Func = function() { ... }
I've written this, which seems to work but I'm really not sure about it's implications:
function sub ( variable, func ) {
var temp_func = function ( args ) {
record_activity( variable );
return func.apply(this,arguments);
}
eval( variable + ' = ' + temp_func );
}
sub( 'Object.Key.Func', function (name) { alert('hi ' + name) } );
Object.Key.Func('test');
If there is a way of doing this without an eval I'd be much happier.
Thanks
I think you can create a wrapper func for each func that you want to track. Hope the following code will help you:
var counter = {};
// assume this is what record_activity has to do.
function record_activity (f, func_name, args) {
counter[func_name] = counter[func_name]===undefined ? 1 : counter[func_name]+1;
}
// assume that you want to keep track of functional call obj.foo and obj.bar
var obj = {
a: 3,
foo: function () {
console.log("foo");
},
bar: function (b) {
console.log("bar", this.a, b);
}
};
function create_func (f, func_name) {
return function () {
record_activity(f, func_name, arguments);
f.apply(obj, arguments);
};
}
for(var prop in obj) {
if (typeof obj[prop] === 'function') {
// create the wrapper func
obj[prop] = create_func(obj[prop], prop);
}
};
// test
for (var i = 0; i < 3; i++) {
obj.foo();
};
for (var i = 0; i < 10; i++) {
obj.bar(i);
};
console.log(counter);