2014-09-24 45 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>