iT邦幫忙

3

WinJS.Promise 物件與 jQuery Deferred 物件比較

vexed 4 年前4964 瀏覽

使用 JavaScript 開發 Windows Store App 的過程中,舉凡非同步動作,例如 IO 、 AJAX , Windows Library for JavaScript ( WinJS ) 吐回的皆是 WinJS.Promise 物件。 WinJS.Promise 物件與 jQuery Deferred 物件皆是依照 CommonJS Promises/A 實作,兩者實作方式不盡相同。但在 jQuery 1.8 將 deferred.then 實作為與 deferred.pipe 相同,並將 deferred.pipe 列為 deprecated 後, WinJS.Promise 物件與 jQuery Deferred 物件越來越相像。
依序對 A 、 B 、 C 三個網址發出 AJAX 為例:

WinJS.Promise 物件

WinJS.xhr({ url: 'A' })
  .then(function() { return WinJS.xhr({ url: 'B' }); })
  .then(function() { return WinJS.xhr({ url: 'C' }); })
  .done(function() { /* ... */ });

jQuery Deferred 物件

$.get('A')
  .then(function() { return $.get('B') })
  .then(function() { return $.get('C') })
  .done(function() { /* ... */ });

再以 localStorage 有值直接取用,無值 AJAX 抓取為例:

WinJS.Promise 物件

var promise;

if(localStorage.x)
    promise = WinJS.Promise.as(localStorage.x);
else
    promise = WinJS.xhr({ url : 'A' }).then(function(data) { /* ... */ });

jQuery Deferred 物件

var promise;

if(localStorage.x)
    promise = $.when(localStorage.x);
else
    promise = $.get('A').then(function(data) { /* ... */ });

差異僅在 WinJS 與 jQuery 發出 AJAX 的方法不同,以及 WinJS 採用 WinJS.Promise.as 將非 Promise 物件變數包覆為 Promise 物件, jQuery 採用 $.when 將非 Deferred 物件變數包覆為 Deferred 物件。

WinJS.Promise 物件與 jQuery Deferred 物件最大差異在於合併多個 Promise / Deferred 物件。 jQuery 以傳入一個或多個參數區分,同時賦予 $.when 「將非 Deferred 物件變數包覆為 Deferred 物件」與「合併多個 Deferred 物件」的功能。 WinJS 則另切 WinJS.Promise.join 方法進行 Promise 物件合併。

同時發出 A 、 B 、 C 三個 AJAX ,待三者皆完成進行下一步為例:

WinJS.Promise 物件

WinJS.Promise.join([
    WinJS.xhr ({ url: 'A' }),
    WinJS.xhr ({ url: 'B' }),
    WinJS.xhr ({ url: 'C' })])
  .done(function() { /* ... */ });

jQuery Deferred 物件

$.when(
    $.get('A'),
    $.get('B'),
    $.get('C'))
  .done(function() { /* ... */ });

注意 WinJS.Promise.join 僅傳入一個為陣列的參數, $.when 則傳入多個參數。

當我們在程式碼中以陣列收集要合併的 Promise / Deferred 物件時,必須使用 apply 呼叫 $.when :

$.when.apply($, arr)
  .done(function() { /* ... */ });

WinJS.Promise.join 則不受影響:

WinJS.Promise.join(arr)
  .done(function() { /* ... */ });

更需注意的是當 $.when 僅傳入一個參數時,功能為「將非 Deferred 物件變數包覆為 Deferred 物件,如傳入參數已是 Deferred 物件,則直接丟回」,與傳入多個參數時,介面並不一致。故如使用陣列收集要合併的 Deferred 物件,而陣列長度可能為 1 時,需特別處理:

$.when.apply($, arr)
  .done(function() {
    if(arr.lengh === 1)
        arguments = arr;
        
    /* ... */    
  });

另一個不同是自製 Promise / Deferred 物件時, WinJS.Promise 物件一切皆在 new WinJS.Promise 時搞定。 jQuery Deferred 物件則保持比較大的彈性,另在 Deferred 物件中利用 Closure 切出一無法改變 Deferred 物件 state 的 Promise 物件

WinJS.Promise 物件

var promise = new WinJS.Promise(function(complete, error, progress) {
        setTimeout(function() {
            // ...
            complete('DEMO');
        }, 3000);
    });

jQuery Deferred 物件

var promise = (function() {
        var deferred = new $.Deferred();
        
        setTimeout(function() {
            // ...
            deferred.resolve('DEMO');
        }, 3000);
        
        return deferred.promise();
    })();

0
wordsmith
iT邦高手 1 級 ‧ 4 年前

最近剛好在研究 promise ,很有參考價值

謝謝謝謝謝謝

0
tony1223
iT邦新手 2 級 ‧ 4 年前

最後一個範例也可以寫成這樣,只是另一種寫法。XD

var promise = $.Deferred(function(deferred) {
setTimeout(function() {
// ...
deferred.resolve('DEMO');
}, 3000);
}).promise();

tony1223 iT邦新手 2 級 ‧ 4 年前 檢舉

然後一般來講 call $.Deferred() 應該是不需要 new 。

vexed iT邦新手 5 級 ‧ 4 年前 檢舉

感謝 TonyQ 大神 m(__ __)m

你的另一種寫法超讚 !!!

0
fillano
iT邦超人 1 級 ‧ 4 年前

Q的做法,可以像Vexed大這樣,只是介面稍微不一樣(in node.js):

<pre class="c" name="code">
var Q = require('q');
var promise = (function() {
    var defered = Q.defer();
    process.nextTick(function() {
        defered.resolved('DEMO');
    });
    return deferred.promise;
})();

不過也提供有一點類似jQuery的方法:

<pre class="c" name="code">
var Q = require('q');
Q.promise(function(resolve, reject, notify) {
    process.nextTick(function() {
        resolve('DEMO');
    });
})
.done(
    function(m) {console.log(m);},
    function(r) {console.log(r);),
    function(p) {console.log(p);}
);

目前各家promise介面都還不太一樣,看看到ECMA7有沒有機會一統江山XD

fillano iT邦超人 1 級 ‧ 4 年前 檢舉

第一個例子有拼錯...再來一次:

<pre class="c" name="code">
var Q = require('q');
var promise = (function() {
    var deferred = Q.defer();
    process.nextTick(function() {
        deferred.resolved('DEMO');
    });
    return deferred.promise;
})();
vexed iT邦新手 5 級 ‧ 4 年前 檢舉

謝謝費大公 m(__ __)m

我要留言

立即登入留言