“return deferred.promise(this)”返回undefined

时间:2014-09-24 21:06:48

标签: javascript jquery jquery-deferred

如果我使用超时解决3秒后的延迟,则以下代码有效...

    var myConstructor = function(){
        var deferred = $.Deferred();
        this.message = "yo";
        setTimeout(function(){
            deferred.resolve();
            }, 3000);
        return deferred.promise(this);
    }
    var myObj = new myConstructor().done(function(){
        console.log(myObj.message);
    })

但是,如果我立即解决延迟而没有超时,如下所示......

    var myConstructor = function(){
        var deferred = $.Deferred();
        this.message = "yo";
        deferred.resolve();
        return deferred.promise(this);
    }
    var myObj = new myConstructor().done(function(){
        console.log(myObj.message);
    })

...然后我得到以下错误:“无法在console.log行上读取未定义的属性'消息'。为什么立即解析延迟导致myObj未定义?

编辑:

在我评论@ T.J之后。克劳德的惊人答案,很明显,这是我需要对代码做出的改变:

    var myConstructor = function(){
        var deferred = $.Deferred();
        this.message = "yo";
        deferred.resolve();
        return deferred.promise(this);
    }
    var myObj = new myConstructor();

    myObj.done(function(){
        console.log(myObj.message);
    })

2 个答案:

答案 0 :(得分:4)

  

但是,如果我立即解决延迟而没有超时,如下所示......

当您在已经已解决的done上致电Deferred时,同步会调用其回调。在您的情况下,这意味着在赋值操作完成之前回调被称为,因此myObj没有值(尚未)。

让我们打破这段代码:

var myObj = new myConstructor().done(function(){
    console.log(myObj.message);
});

...当你(不使用setTimeout时)发生的事情:

  1. 一旦进入范围(在按行的顺序执行该代码行之前),就会创建一个名为myObj的变量,其值为undefined
  2. 评估
  3. new myConstructor(),其中:
    • 创建由myConstructor.prototype
    • 支持的新空白对象
    • 调用myConstructorthis引用该对象
  4. 创建匿名函数(但不执行)。
  5. 调用
  6. done,传入匿名函数。
    • 由于Deferred 已经已经解决,done会立即调用回调而不是等待。 (我恰好是那些不同意这种设计选择*的人,但这就是jQuery的承诺/推迟的设计。)
    • 回调访问myObj,其值为undefined,因此会抛出异常
  7. done返回,myObj获取其返回值。
  8. 但是当你使用setTimeout

    1. 一旦进入范围(在按行的顺序执行该代码行之前),就会创建一个名为myObj的变量,其值为undefined
    2. 评估
    3. new myConstructor(),其中:
      • 创建由myConstructor.prototype
      • 支持的新空白对象
      • 调用myConstructorthis引用该对象
      • 创建计时器回调但尚未执行它
    4. 创建匿名函数(但不执行)。
    5. 调用
    6. done,传入匿名函数。由于Deferred尚未(尚未)解析,因此不会调用回调。
    7. done返回,myObj获取其返回值。
    8. 一段时间后,计时器触发并解析Deferred
      • 调用回调。
      • 回调访问myObj,其值为步骤5并成功使用。

    9. *“设计选择” - 如果done已经解决,则Deferred同步调用回调是一种设计选择。它以性能换取语义。基本上,jQuery开发人员有两个选择:

      1. 同步调用回调,意味着语义混乱(有时它被同步调用,其他时间是异步调用),或者

      2. 即使状态已知(通过setTimeout(..., 0)或类似),也会异步调用回调,这意味着保留了语义(回调始终异步),但性能可能会受到影响(浏览器有时会在setTimeout回调上施加最小4毫秒的延迟,但不像HTML5规范那样一致。)

      3. 哪个是对的?这完全是一个意见问题。主观上,对于 me ,语义应该胜利:可能异步的回调应该总是是异步的 - 如果他们做出了这个选择,你的代码不会遇到问题,因为在分配给myObj之后,回调总是会发生。但是jQuery开发人员,他们是一个聪明的人,做出了另一个选择,这是他们的权利。 : - )

答案 1 :(得分:1)

尝试使用deferred.resolveWith()

var myConstructor = function(){
        var deferred = $.Deferred();
        this.message = "yo";
        deferred.resolveWith(this, [this.messgae]);
        return deferred.promise();
    }
    var myObj = new myConstructor().done(function(){
        console.log(this.message); // `yo`
        $("body").append(this.message);
    });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
  1http://api.jquery.com/deferred.resolveWith/

修改

还可以resolve(this.message)

    var myConstructor = function(){
        var deferred = $.Deferred();
        this.message = "yo";
        deferred.resolve(this.message);
        return deferred.promise();
    }
    var myObj = new myConstructor();

    myObj.done(function(msg){
        console.log(msg); // `yo`
    });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>