Chaining promises without using 'then' with Q library - javascript

I'm trying to chain Q promises without 'then', so eventually the chain would look like this:
var foo = new Foo();
foo
.method1()
.method2()
.method3();
How to implement foo's methods so each one is executed once the promise of the previous one is resolved?
This question was marked as exact duplicate of this one but I'm trying to implement this using Q lib, not jQuery.

I'm not sure if you are going to gain anything with this.
I suppose that you can do something like this:
function Foo() {
var self = this,
lastPromise = Promise.resolve();
var chainPromise = function(method) {
return function() {
var args = Array.prototype.slice.call(arguments))
lastPromise = lastPromise.then(function(){
return method.apply(self, args);
});
return self;
}
}
this.method1 = chainPromise(function() {
// do the method1 stuff
// return promise
});
this.method2 = chainPromise(function() {
// do the method2 stuff
// return promise
});
// etc...
}

Related

Exporting a function in NodeJS, different attempts returned different errors: undefined, TypeError properties undefined, and promise pending [duplicate]

I was wondering what the best approach is for configuring a module export. "async.function" in the example below could be a FS or HTTP request, simplified for the sake of the example:
Here's example code (asynmodule.js):
var foo = "bar"
async.function(function(response) {
foo = "foobar";
// module.exports = foo; // having the export here breaks the app: foo is always undefined.
});
// having the export here results in working code, but without the variable being set.
module.exports = foo;
How can I export the module only once the async callback has been executed?
edit
a quick note on my actual use-case: I'm writing a module to configure nconf (https://github.com/flatiron/nconf) in an fs.exists() callback (i.e. it will parse a config file and set up nconf).
Your export can't work because it is outside the function while the foodeclaration is inside. But if you put the export inside, when you use your module you can't be sure the export was defined.
The best way to work with an ansync system is to use callback. You need to export a callback assignation method to get the callback, and call it on the async execution.
Example:
var foo, callback;
async.function(function(response) {
foo = "foobar";
if( typeof callback == 'function' ){
callback(foo);
}
});
module.exports = function(cb){
if(typeof foo != 'undefined'){
cb(foo); // If foo is already define, I don't wait.
} else {
callback = cb;
}
}
Here async.function is just a placeholder to symbolise an async call.
In main
var fooMod = require('./foo.js');
fooMod(function(foo){
//Here code using foo;
});
Multiple callback way
If your module need to be called more than once you need to manage an array of callback:
var foo, callbackList = [];
async.function(function(response) {
foo = "foobar";
// You can use all other form of array walk.
for(var i = 0; i < callbackList.length; i++){
callbackList[i](foo)
}
});
module.exports = function(cb){
if(typeof foo != 'undefined'){
cb(foo); // If foo is already define, I don't wait.
} else {
callback.push(cb);
}
}
Here async.function is just a placeholder to symbolise an async call.
In main
var fooMod = require('./foo.js');
fooMod(function(foo){
//Here code using foo;
});
Promise way
You can also use Promise to solve that. This method support multiple call by the design of the Promise:
var foo, callback;
module.exports = new Promise(function(resolve, reject){
async.function(function(response) {
foo = "foobar"
resolve(foo);
});
});
Here async.function is just a placeholder to symbolise an async call.
In main
var fooMod = require('./foo.js').then(function(foo){
//Here code using foo;
});
See Promise documentation
An ES7 approach would be an immediatly invoked async function in module.exports :
module.exports = (async function(){
//some async initiallizers
//e.g. await the db module that has the same structure like this
var db = await require("./db");
var foo = "bar";
//resolve the export promise
return {
foo
};
})()
This can be required with await later:
(async function(){
var foo = await require("./theuppercode");
console.log(foo);
})();
ES6 answer using promises:
const asyncFunc = () => {
return new Promise((resolve, reject) => {
// Where someAsyncFunction takes a callback, i.e. api call
someAsyncFunction(data => {
resolve(data)
})
})
}
export default asyncFunc
...
import asyncFunc from './asyncFunc'
asyncFunc().then(data => { console.log(data) })
Or you could return the Promise itself directly:
const p = new Promise(...)
export default p
...
import p from './asyncModule'
p.then(...)
Another approach would be wrapping the variable inside an object.
var Wrapper = function(){
this.foo = "bar";
this.init();
};
Wrapper.prototype.init = function(){
var wrapper = this;
async.function(function(response) {
wrapper.foo = "foobar";
});
}
module.exports = new Wrapper();
If the initializer has error, at least you still get the uninitialized value instead of hanging callback.
You can also make use of Promises:
some-async-module.js
module.exports = new Promise((resolve, reject) => {
setTimeout(resolve.bind(null, 'someValueToBeReturned'), 2000);
});
main.js
var asyncModule = require('./some-async-module');
asyncModule.then(promisedResult => console.log(promisedResult));
// outputs 'someValueToBeReturned' after 2 seconds
The same can happen in a different module and will also resolve as expected:
in-some-other-module.js
var asyncModule = require('./some-async-module');
asyncModule.then(promisedResult => console.log(promisedResult));
// also outputs 'someValueToBeReturned' after 2 seconds
Note that the promise object is created once then it's cached by node. Each require('./some-async-module') will return the same object instance (promise instance in this case).
Other answers seemed to be partial answers and didn't work for me. This seems to be somewhat complete:
some-module.js
var Wrapper = function(){
this.callbacks = [];
this.foo = null;
this.init();
};
Wrapper.prototype.init = function(){
var wrapper = this;
async.function(function(response) {
wrapper.foo = "foobar";
this.callbacks.forEach(function(callback){
callback(null, wrapper.foo);
});
});
}
Wrapper.prototype.get = function(cb) {
if(typeof cb !== 'function') {
return this.connection; // this could be null so probably just throw
}
if(this.foo) {
return cb(null, this.foo);
}
this.callbacks.push(cb);
}
module.exports = new Wrapper();
main.js
var wrapper = require('./some-module');
wrapper.get(function(foo){
// foo will always be defined
});
main2.js
var wrapper = require('./some-module');
wrapper.get(function(foo){
// foo will always be defined in another script
});

Dynamically add argument to function in javascript

I'm testing my app, I have a dependency injection system that works like this (I paste here a very simplified version)
test.start = function(callback) {
// do stuff;
load(callback);
}
function load(callback) {
var args = ...// read callback's arguments
// args = ['moduleA', 'moduleB'];
var injectedArgs = args.map(function(a) {
return require('./lib/' + a);
});
// call function with deps injected
callback.apply(null, injectedArgs);
}
// test.js
test.start(function(moduleA, moduleB) {
moduleA.functionA();
moduleB.functionB();
});
What I need to do is an async job before calling the callback and start the test, in this particular case, that would be
test.start = function(callback) {
// do stuff;
load(function(moduleA, moduleB, moduleC) {
moduleC.resetData(function() {
return callback(moduleA, moduleB);
})
});
}
I need to make this dynamic, for all the test that have different modules as their callback arguments.
Requirements are
I don't want to change test definitions
I must call load only once per test
So basically I need to create the middle function that add moduleC argument, run the resetData function and call the original callback.
I can build the dynamically extended argument list using
var fn = new Function(newArgumentList, /*function body*/) but I can't attach the correct function body (using a string) because I lose the original callback context.
UPDATE
The DI library is modularity and it reads the callback definition to grab dependency. So basically, given the callback function(foo, bar) { ... } I need to create a new function(foo, bar, db) { db.resetData(function() { return callback(foo, bar) } )}
If load, itself, is something you can change (e.g., not part of the test definitions), you'll be glad to know this is quite easy to do: You use Function#apply:
function load(callback) {
var origCallback;
if (/*flag saying you need to do this*/) {
origCallback = callback;
callback = function() {
/*...do your injected work here...*/
return origCallback.apply(this, arguments); // `arguments` looks like pseudo-code, but it isn't; it's an identifier created in the function's scope (for non-arrow functions)
};
}
var args = ...// read callback's arguments
// args = ['moduleA', 'moduleB'];
var injectedArgs = args.map(function(a) {
return require('./lib/' + a);
});
// call function with deps injected
callback.apply(null, injectedArgs);
}
You can even do it if you can't change load, provided you can update the load symbol to make it point at a new function:
var oldload = load;
load = function() {
/*...do your reset work here...*/
return oldload.apply(this, arguments);
};
If you're curious, yes, you can update the symbols created by function declarations (and yes, that's per spec, not "tricky"). E.g., this is perfectly valid:
function foo() {
console.log("I'm foo");
}
var oldFoo = foo;
foo = function() {
console.log("I'm the new foo");
return oldFoo.apply(this, arguments);
};
foo(); // "I'm the new foo", then "I'm foo"
Example:
function foo() {
snippet.log("I'm foo");
}
var oldFoo = foo;
foo = function() {
snippet.log("I'm the new foo");
return oldFoo.apply(this, arguments);
};
foo(); // "I'm the new foo", then "I'm foo"
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="//tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
I found out the solution at the end.
Since modularity supports angular-like injection like this
['modules/a', 'modules/b', function(moduleA, moduleB) {
// use modules here
}]
I could write my custom callback like this
var customCallback = function() {
var args = Array.prototype.slice.call(arguments);
var injectedDBModule = args.pop();
injectedDBModule.resetData(function() {
// call the original callback function
return originalCallback.apply(null, args);
}
}
var loadCallback = originalInjectedModules; // ['moduleA', 'moduleB']
loadCallback.push('db');
loadCallback.push(customCallback);
modularity.load(loadCallback);

How to call the same function repeatedly?

I want to call a function twice but not through the traditional way. A quick example of what I would be looking to do is below:
var myfunc = {
copy: function(message){
console.log(message);
}
}
myfunc.copy('hello').copy('world');
// result 'hello'
// result 'world'
Is this even possible?
Yes, but you should return the correct object:
var myfunc = {
copy: function(message){
console.log(message);
return this;
}
};
myfunc.copy('hello').copy('world');
// hello
// world
This technique is also known as Method chaining.
No, this will fail because .copy() doesn't return anything, so the second .copy() would throw an undefined error.
Try this:
var myfunc = {
copy: function(message){
console.log(message);
return this;
}
}
You need to chain this, You can also look up to jquerys $.fn how they create method chaining simply return this as this object is your myFunc variable and is used again by the next function
var myfunc = {
copy: function(message){
console.log(message);
return this;
}
};
myfunc.copy('hello').copy('world');
As other answers have pointed out, you need to return this in order to be able to call the same or additional functions on the same object.
You can do this a bit more easily (?) via a higher-order function which we will call "chainify", which takes care of returning this for you:
function chainify(fn) {
return function() {
fn.apply(this, arguments);
return this;
};
}
You can chainify your object methods in various ways, but here's one:
var myfunc = {
init: function() {
this.copy = chainify(this.copy);
return this;
},
copy: function(message){
console.log(message);
}
}.init();
This has the minor advantage that each and every method does not need to be cluttered with the return this at the end, and you don't run the risk of forgetting to do so.
It's called method chaining. Here's a blog that talks about this that you should be able to read and use to answer your question.
Basically you will need to use return this; to return the current object so the next method can use it.
// define the class
var Kitten = function() {
this.name = 'Garfield';
this.color = 'brown';
this.gender = 'male';
};
Kitten.prototype.setName = function(name) {
this.name = name;
return this;
};
Method chaining in JavaScript
This is known a Builder design pattern where in you cascade methods. Google it if it helps.

Chained Promises (Q deferred) with Object / Prototype

I have a simple Javascript (Node) object, which has a function that sets a property on the object and returns a promise.
Note, for this example I've removed the actual async call that needs to be deferred, as it doesn't effect the outcome.
var q = require("Q");
var Foo = function(){
this.bar = false;
return this;
};
Foo.prototype.set = function(){
var d = q.defer();
this.bar = true;
d.resolve();
return d.promise;
};
Foo.prototype.check = function(){
var d = q.defer();
console.log(this.bar);
d.resolve();
return d.promise;
};
When the above is called in a promise-defeating way like below, this.bar is true (as expected).
var foo = new Foo();
foo.set().then(function(){
foo.check();
});
However, when it's called in a chain, it's undefined:
foo.set().then(foo.check);
I'm curious to understand what's causing this. My best guess is a closure issue with the way that my object's methods are chained together.
In most cases, I'd pass the value to resolve/reject and go from there. In this case, I want to run some data through a series of sequential functions (some requiring deferment). Rather than passing the output to each function in the chain, I want to store and update the data in the object itself.
Any clarity would be much appreciated!
The problem you calls callback without context and this is just global object
As a solution you need to bind context like this
foo.set().then(foo.check.bind(foo));

chain functions directly to newly created object instance in Javascript

In my ongoing saga to understand more about Object Oriented Javascript - I came across a question about creating a class to respond to the following API:
var foo = new bar().delay(750).start().then(onComplete);
var bar = function() {
this.delay(function(per) {
//...
};
}
Can someone with more experience than me please describe how to create class that would respond to this? I have never seen chaining like this and can't find any information online :(
This chaining is accomplished by returning this in your functions :
this.delay = function(per) {
//...
return this;
};
If you want to stick to first line code, then your constructor should be named bar :
var bar = function() {
this.delay = function(per) {
//...
return this;
};
this.start = function() {
...
return this;
};
}
See demonstration (open the console)
The secret to method chaining is to return this from each method that you want to be able to chain with. That allows the next method to be this.method() automatically. Your definition of the bar object would look something like this shell:
function bar() {
// bar initialization code here
}
bar.prototype = {
delay: function(amt) {
// delay code here
return(this);
},
start: function() {
// start code here
return(this);
},
then: function(fn) {
// then code here
fn();
return(this);
}
};
var foo = new bar().delay(750).start().then(onComplete);
In your example, new bar() is executed and it returns a pointer to the new bar object.
Using that new object pointer, the .delay(750) method is called on that object. That method then also returns the object so the .start() method is called on the return value from .delay(750) which is still the same object and so on...

Categories