2014-09-24 74 views
1

,如果我使用超时如下解决秒3推迟后,下面的代码工作...“返回deferred.promise(本)”是返回undefined

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

...然后我得到以下errror:在执行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); 
    }) 
+1

,因为完成回调被立即调用,所以'我在调用函数之前不会创建Obj'变量 – 2014-09-24 21:12:57

+0

此外,设计并不好。我宁愿建议在构造函数上有一个“静态”函数,它返回一个承诺,并用“类”的新实例解决它。 – 2014-09-24 21:18:12

+0

@FelixKling我在理解你的意思时有点麻烦。你认为你可以输入一些代码吗? – 2014-09-24 21:32:58

回答

4

,但如果我解决立即推迟不超时如下...

当您拨打doneDeferred已解决,它同步调用其回调。在你的情况下,这意味着在分配操作完成之前回调被称为,因此myObj没有值(尚)。

我们把这个代码:

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

...到时会发生什么(如果你不使用setTimeout):

  1. 在进入范围(该行的代码之前是按照事物的分步顺序​​执行的),则使用值undefined创建一个名为myObj的变量。
  2. new myConstructor()被评估,其中:
    • 创建由myConstructor.prototype
    • 呼叫myConstructorthis指的是对象
  3. 匿名功能被创建(但不执行)备份一个新的空白对象。
  4. done被调用,传入匿名函数。
    • 由于Deferred已解决,因此done立即调用回调而不是等待。 (我恰好在一群不同意这个设计选择的人中,但这是jQuery的承诺/延期设计。)
    • 回调访问myObj,其值为undefined,所以抛出异常
  5. done回报和myObj获取其返回值。

但是,当使用setTimeout

  1. 在进入范围(前的代码行,在步骤一步顺序的事情被执行),可变称为myObj创建值为undefined
  2. new myConstructor()进行评估,其中:
    • 创建由myConstructor.prototype
    • 呼叫myConstructorthis指的是物体
    • 支持一个新的空白对象创建计时器回调,但不(还)执行它
  3. 创建匿名函数(但未执行)。
  4. done被调用,传入匿名函数。由于Deferred尚未解决,因此不会调用回调。
  5. done退货和myObj获得其返回值。
  6. 一段时间后,计时器触发并解决Deferred
    • 回调被调用。
    • 回调访问myObj,它具有步骤5中的值并成功使用它。

* “设计的选择” - 有done调用回调同步如果Deferred已经被解决的是一个设计选择。它与性能交换了语义。基本上,jQuery的开发者有两种选择:

  1. 调用回调同步,这意味着语义混乱(有时这就是所谓的同步,异步等次),或

  2. 调用回调异步即使状态是已知的(通过setTimeout(..., 0)或类似的),这意味着语义被保留(回调是总是异步),但性能可能会受到影响(浏览器有时在setTimeout回调上施加最小延迟4ms,尽管不如HTML5规范会让他们这样做)。

这是对的吗?这完全是一个意见问题。主观上,对,语义应该赢:一个回调可能是异步的,应始终是异步  —,如果他们犯了这样的选择,你的代码不会有它有问题,因为之后回调总是会发生myObj完成。但是jQuery开发人员是一群聪明人,他们做出了另一种选择,这是他们的权利。 :-)

+0

关于“设计选择”的补充,我相信Promise/A +规范和ES6完全遵循以下规则:回调总是必须以异步方式执行,与承诺的状态无关。 – 2014-09-24 21:51:34

+0

@ t-j-crowder“该回调访问myObj,该值具有未定义的值,因此引发异常”。 'undefined',由于没有参数传递给'resolve()'或'resolveWith()'而返回?例如,试试'deferred.resolve(this.message);',见post。谢谢 – guest271314 2014-09-24 21:55:52

+0

@FelixKling:是的。 jQuery的承诺因Promises/A +而有所不同。 :-) – 2014-09-24 21:55:53

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>