4
\$\begingroup\$

I couldn't find an example on how to have a custom function inherit jQuery's functions while leaving the original jQuery function I'm inheriting from unchanged (I wanted my function to have a read but didn't want it to get added to $.Deferred)

This works, but I really suspect it isn't the right way to do it.

var DeferredFileEntry = function(){
    var def = $.Deferred();
    var promise = def.promise;

    def.read = function(textFnCallback){ // <--this is the method I'm adding
        return $.Deferred(function(newDefer){
            var newDefer = $.Deferred();
            def.done(function(fileEntry){
                fileEntry.getText()
                    .done(function(text){
                        textFnCallback(text);
                        newDefer.resolve(fileEntry);
                    });
            });
        }).promise();
    };
    def.promise = function(){
        //expose the custom method to the returned promise
        promise.read = def.read;
        return promise();
    };
    return def;
};

An example usage:

var getFileEntry = function(){
    var def = DeferredFileEntry();
    libraryCall() // that promises to resolve with a FileEntry object
        .done(function(fileEntry){
            def.resolve(fileEntry);
        });
    return def.promise();
}

getFileEntry() 
    .read(function(text){
        console.log("reading content:" + text);
    })
    .read(function(text){
        //chained, 
        //doesn't run until the previous one has resolved
        console.log("reading content again: "+text);
    });
\$\endgroup\$
  • \$\begingroup\$ Did you try using jQuery.sub? \$\endgroup\$ – Larry Battle Sep 19 '12 at 19:51
  • 1
    \$\begingroup\$ facepalm didn't know about that X( \$\endgroup\$ – CheapSteaks Sep 19 '12 at 20:27
  • \$\begingroup\$ Where's the code for getFileEntry? Does it just return DeferredFileEntry? \$\endgroup\$ – Larry Battle Sep 19 '12 at 20:37
  • \$\begingroup\$ ah right, it returns a DeferredFileEntry promise. I should edit that in \$\endgroup\$ – CheapSteaks Sep 20 '12 at 0:13
  • \$\begingroup\$ Why are you receiving a parameter that you never use? function (newDefer) { var newDefer = $.Deferred(); \$\endgroup\$ – ANeves wants peace for Monica Sep 21 '12 at 10:55
5
\$\begingroup\$

Tips:

1)

Returning this from a function of an object instance allows for the method to become chainable.

Example:

var File = function(){
    this.readCallCount = 0;
    return this;
};
File.prototype.readLine = function(){
    this.readCallCount++;
    console.log( "read line: " + this.readCallCount );
    return this;
};
var fileA = new File();
fileA.readLine().readLine().readLine();
/*
output:
read line: 1
read line: 2
read line: 3
*/

2)

The passed arguments from deferred.resolve() are saved until deferred.resolve() is invoked again. Therefore the callbacks stored from deferred.done() will be passed the same values from the last call from deferred.resolve().

Example:

var arr = [];
function log(str){
    return arr.push( str );
}
function fn1(time) {
  return log( "fn1 called at " + time );
}
function fn2(time) {
  return log( "fn2 called at " + time );
}
var dfd = $.Deferred();
dfd.done(fn1);
dfd.resolve( +(new Date()) );

setTimeout(function(){
    dfd.done(fn2);
    console.log( arr.join( ", " ) );
}, 2000);
/*
 outputs after 2 seconds
 fn1 called at 1348177948625, fn2 called at 1348177948625
*/

Different results can be produced if a function or object is passed to deferred.resolve().

Example:

var arr = [];
function log(str){
    return arr.push( str );
}
function fn1(getTimeFunc) {
  return log( "fn1 called at " + getTimeFunc() );
}
function fn2( getTimeFunc ) {
  return log( "fn2 called at " +  getTimeFunc() );
}
var dfd = $.Deferred();
dfd.done(fn1);
dfd.resolve(function(){
    return +(new Date()) 
});

setTimeout(function(){
    dfd.done(fn2);
    console.log( arr.join( ", " ) );
}, 2000);
/*
output after 2 seconds
fn1 called at 1348178107859, fn2 called at 1348178109860
*/

3)

The callbacks passed to deferred.done() are saved in a callback queue called $.Callbacks(). The queue is set like so, jQuery.Callbacks("once memory"). This means that all the callbacks stored in the queue will only fired once and that every callback receieves the same arguments. Also notes that the callbacks are fired in order of First in First out.

Final Code:

This might work. Instead of extending the deferred object to include a read method, I opted to return an plain object.

Note: My code is assuming that libraryCall() and fileEntry.getText() return a deferred.promise() or deferred object.

Code:

var FileEntryObj = function(){
    this.libraryCallObj = libraryCall();
};
FileEntryObj.prototype.read = function(fn){
    this.libraryCallObj.done( function( fileEntry ){
        return fileEntry.getText().done( fn );
    });
    return this;
};
var getFileEntry = function(){
    return new FileEntryObj();
};

Usage:

getFileEntry().read(function (text) {
    console.log("reading content:" + text);
}).read(function (text) {
    console.log("reading content again: " + text);
});
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.