12

I want to implement basic Deferred object without using jQuery. Here i will be implementing only done and fail callbacks, with resolve and reject functions. and ofCourse associating promise method with this function.

i am doing the following implementation in pure js (Edited) :

function Deferred() {
    var d = {};
    d.resolve = function() {
        d.done(arguments);
    }
    d.reject = function() {
        d.fail(arguments);
    }
    d.promise = function() {
        var x = {};
        x.done = function(args) {
            return args;
        }
        x.fail = function(args) {
            return args;
        }
        return x;
    }
    return d;
}


var v;

var setVal = function() {
    var d = new Deferred();
    setTimeout(function() {
        v = 'a value';
        d.resolve(this);
    }, 5000);
    return d.promise();
};

setVal().done(function() {
    console.log('all done :' + v);
});

But the above gives the error : Object #<Object> has no method 'fail'

I know the returned object 'd' of Deferred() function does not have method done(). And if i returns d.promise from Deferred() this will not have resolve and reject functions.

Please point out what error i am making to achieve the simple objective of Deferred object.

Here is the fiddle i am doing : http://jsfiddle.net/SyEmK/14/

8
  • If you create an instance with new then you should add the methods to the prototype. The constructor returns an instance, even if you return d. You may want var d = Deferred() Commented Aug 7, 2013 at 7:10
  • You don't have a method called done. If you look at your object, you have resolve, reject, and promise. setVal() returns an instance of your object with those 3 methods. Commented Aug 7, 2013 at 7:14
  • jQuery isn't the only library to implement promises. Commented Aug 7, 2013 at 7:14
  • by mistake i had put the previous coed.. please review the new edited code.. Commented Aug 7, 2013 at 7:14
  • 1
    @JayC I want to implement in pure js. (just as a constraint, i have to follow).. Commented Aug 7, 2013 at 7:16

3 Answers 3

21
function Deferred(){
  this._done = [];
  this._fail = [];
}
Deferred.prototype = {
  execute: function(list, args){
    var i = list.length;

    // convert arguments to an array
    // so they can be sent to the
    // callbacks via the apply method
    args = Array.prototype.slice.call(args);

    while(i--) list[i].apply(null, args);
  },
  resolve: function(){
    this.execute(this._done, arguments);
  },
  reject: function(){
    this.execute(this._fail, arguments);
  }, 
  done: function(callback){
    this._done.push(callback);
  },
  fail: function(callback){
    this._fail.push(callback);
  }  
}


var v;

var setVal = function() {
    var d = new Deferred();
    setTimeout(function() {
        v = 'a value';
        d.resolve(this);
    }, 5000);
    return d;
};

setVal().done(function() {
    console.log('all done :' + v);
});
Sign up to request clarification or add additional context in comments.

1 Comment

If you want to come even closer to the jQuery behavior, you might want to add a "promise" function that returns an object with only "done" and "fail" (so that the calling function won't have access to resolving/rejecting). Also - you can return "this" from done/fail, to allow chaining.
1

I think it's good to un-assign the callbacks after they are called to avoid memory leaks and also execute ones added later straight away.

function deferred() {
  let thens = []
  let catches = []

  let status
  let resolvedValue
  let rejectedError

  return {
    resolve: value => {
      status = 'resolved'
      resolvedValue = value
      thens.forEach(t => t(value))
      thens = [] // Avoid memleaks.
    },
    reject: error => {
      status = 'rejected'
      rejectedError = error
      catches.forEach(c => c(error))
      catches = [] // Avoid memleaks.
    },
    then: cb => {
      if (status === 'resolved') {
        cb(resolvedValue)
      } else {
        thens.unshift(cb)
      }
    },
    catch: cb => {
      if (status === 'rejected') {
        cb(rejectedError)
      } else {
        catches.unshift(cb)
      }
    },
  }
}

const d = deferred()

setTimeout(() => {
  d.resolve('good')
}, 1000)

// Will be called after 1s
d.then(value => console.log('#1 resolved!', value))

setTimeout(() => {
  // Will be called after 3s and executed right away as it's already resolved
  d.then(value => console.log('#2 resolved!', value))
}, 3000)

Comments

1

I never used jquery Deferred, but in case anyone comes here looking for a clone of angularjs $q.defer(), all you have to do is this

function Defer() {
    const self = this;
    self.promise = new Promise((resolve, reject) => {
        self.resolve = resolve;
        self.reject = reject;
    });
}

//use it like this
const deferred = new Defer();
//then call deferred.resolve(), deferred.reject() and use deferred.promise

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.