2014-11-24 111 views
1

在下面的代碼中,每當任何一個延遲對象失敗時,我需要退出for循環。每當循環大寫字母redyellow顏色失敗,我想避免其他延遲執行。當任何jQuery延遲失敗時退出for循環

var colours = ['violet', 'indigo', 'blue', 'green', 'yellow', 'orange', 'red']; 

var capitalize = function(text) { 
    var deferred = $.Deferred(), 
    delay = (1 + Math.floor(Math.random() * 5)) * 500; 

    setTimeout(function() { 
    if (text === 'red' || text === 'yellow') { 
     deferred.reject('error colour', true); 
    } else { 
     deferred.resolve(text.toUpperCase()); 
    } 
    }, delay); 

    return deferred.promise(); 
} 

var deferreds = [], 
    result = []; 

for (var i = 0; i < colours.length; i++) { 
    var deferred = capitalize(colours[i]); 

    deferred.done(function(t) { 
    console.log(t); 
    result.push(t); 
    }).fail(function(e) { 
    console.log(e); 
    }); 

    deferreds.push(deferred); 
} 

$.when.apply($, deferreds).done(function(){ 
    console.log(result); 
}).fail(function(e){ 
    console.log(e); 
}); 

Link to jsfiddle

編輯:

我建立與骨幹科爾多瓦基於移動應用程序。我必須使用localStorage進行持久化,並且必須在持久化之前加密所有數據。雖然使用本地存儲來存儲/檢索數據是同步的,但要加密/解密數據,我必須調用異步的本機設備API。我想過使用Backbone.localstorage插件,但它不是針對異步開發的......所以沒什麼用處!

我正在開發自己的插件。除了一種方法,我幾乎完成了大部分代碼,即findAll

findAll方法返回存儲在localstorage中的所有模型。

// exiting method in the Backbone.Localstorage plugin 
extend(Backbone.LocalStorage.prototype, { 
    // ... 
    findAll: function() { 
    var result = []; 
    for (var i = 0, id, data; i < this.records.length; i++) { 
     id = this.records[i]; 
     data = this.serializer.deserialize(this.localStorage().getItem(this._itemName(id))); 
     if (data != null) result.push(data); 
    } 
    return result; 
    } 
}); 

在localstorage中,所有模型都被加密並存儲爲字符串。在反序列化回模型之前,我必須解密它們。 this.crypto類解密從localstorage返回的字符串,並反序列化回模型。加密/解密是aysnc,所以它返回一個承諾。

// Method in my new plugin 
findAll: function() { 
    var deferred = $.Deferred(), 
    deferreds = [], 
    result = [], 
    errors = []; 

    // this.records contains all the model ids 
    for (var i = 0, id, data; i < this.records.length; i++) { 
    id = this.records[i]; 

    // this.crypto return a promise with the backbone model as parameter 
    var d = this.crypto.deserialize(this.localStorage().getItem(this._itemName(id))); 

    d.done(function(model) { 
     // I've to maintain the order in result so storing the index 
     result.push({ index: i, model: model }); 
    }).fail(function(error) { 
     errors.push(error); 
    }); 

    deferreds.push(d); 
    } 

    $.when.apply($, deferreds) 
    .done(function() { 
     // TODO: sort the result based on i and resolve it 
     deferred.resolve(sortedResult); 
    }) 
    .fail(...); 

    return deferred.promise(); 
} 

解密時可能會出現錯誤,在這種情況下,我必須停止整個過程。

回答

1

您的新findAll方法將簡化如下:

findAll: function() { 
    var promises = this.records.map(function(record) { 
     return this.crypto.deserialize(this.localStorage().getItem(this._itemName(record))); 
    }); 
    return $.when.apply(null, promises).then(function() { 
     return Array.prototype.slice.apply(arguments);//convert arguments to array 
    }); 
} 

如果成功,這將返回你想要的順序結果數組的一個承諾 - 無需任何排序。任何個人異步故障將導致返回的承諾,被拒絕。但是,這種拒絕將不會阻止其他異步調用,這些調用已經完成。

爲了允許未能禁止進一步呼叫this.crypto.deserialize(...),您必須串行執行呼叫(以菊花鍊形式),從而允許每次呼叫的結果確定下一個操作過程。

findAll: function() { 
    var results = []; 
    return this.records.reduce(function(promise, record) { 
     return promise.then(function(result) { 
      if(result) results.push(result); 
      return this.crypto.deserialize(this.localStorage().getItem(this._itemName(record))); 
     }); 
    }, $.when()).then(function() { 
     return results; 
    }); 
} 

像以前一樣,在成功這將返回你想要的順序結果數組的一個承諾。

由於每個執行階段只有在前一階段成功,進一步抑制通話將在異步故障時自動發生。雖然.reduce(...)建成了那麼無條件鏈,拒絕將迫使環比下降了失敗的路徑 - thens到失敗的權利將被跳過(他們沒有失敗處理程序)。

事情略有不同,如果你需要根據測試result強制失敗,但整體格局在本質上是一樣的。

+0

您的回答非常有幫助。謝謝。 – VJAI 2014-11-25 05:49:03

+0

我得到**不確定是不是一個功能**錯誤在這行'返回Array.prototype.apply(參數)'我覺得應該是'Array.prototype.slice.call(參數)'吧? – VJAI 2014-11-25 06:44:18

+0

是的,正是如此,我忘記了.slice'。發現得好。 – 2014-11-25 09:30:43

1

「只要......它失敗,我想避免其它deferreds得到執行」

想想了一會兒。這是不可能的。

  • 您有一個for循環,它創建並運行異步函數(即「延遲」)。
  • 循環之前即使在第一這些函數返回(即「做出決議」)的完成
  • 當第一個函數返回一個負結果(即「承諾被拒絕」),有什麼事情你可能在這點上做阻止他人執行?這已經太晚了。

有幾種可能的解決方案,但它們都依賴於更好的問題定義。

+0

所以..在這種情況下,執行所述deferreds逐個(順序地)是一個很好的選擇? – VJAI 2014-11-24 09:38:01

+0

不一定。我的意思是,如果你*不需要異步行爲,爲什麼還要首先考慮延遲呢?但是,如果您希望異步行爲......正如我所說,您的問題定義/說明缺乏。解釋你想達到的目標。 – Tomalak 2014-11-24 09:40:38

+0

我試圖解釋問題中的真實背景。希望它能幫助你理解。 – VJAI 2014-11-24 10:13:23