不使用jQuery实现Deferred对象

12

我希望在不使用jQuery的情况下实现基本的Deferred对象。在这里,我将只实现done和fail回调函数,并使用resolve和reject函数,当然还要将promise方法与此函数关联。

我正在纯js中进行以下实现(已编辑):

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);
});

然而上述代码会报错:Object #<Object> has no method 'fail'

我知道Deferred()函数返回的对象'd'没有done()方法。如果我从Deferred()函数中返回'd.promise',那么这个对象将没有resolvereject函数。

请指出我犯了什么错误以实现Deferred对象的简单目标。

这是我正在做的fiddle:http://jsfiddle.net/SyEmK/14/


你没有一个名为done的方法。如果你看一下你的对象,你有resolve、reject和promise。setVal()返回一个带有这3个方法的对象实例。 - THEtheChad
jQuery并不是唯一实现Promise的库。 - JayC
1
@JayC 我想要使用纯JS实现(只是一个限制,我必须遵循)。 - codeofnode
现在的问题是你正在调用d.done(),但它仍然没有叫做done的方法。你创建了另一个名为x的变量,其中有x.done(),但你没有调用它。 - THEtheChad
你可以查看这个实现 - Arun P Johny
显示剩余3条评论
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);
});

5
如果你想让它更接近 jQuery 的行为,你可能希望添加一个“promise”函数,它返回一个只有“done”和“fail”的对象(这样调用函数就不能访问解决/拒绝)。另外,你可以从 done/fail 返回 "this",以允许链式调用。 - lyosef

1

我从未使用过jquery Deferred,但是如果有人在这里寻找angularjs $q.defer()的克隆版本,你只需要这样做

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

1
我认为及时取消回调分配可以避免内存泄漏,并且直接执行后面添加的回调函数也是一个好习惯。

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)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接