2016-12-08 67 views
0

我想上傳圖片列表。我有圖像存儲在一個數組(稱爲圖像)。異步帖子 - 處理回覆

我有預覽顯示在屏幕上。

我想要做的是順序上傳它們,當它們完成上傳時,我想設置一個標誌。當設置此標誌(感謝Knockout的力量)時,圖像從列表中消失。

但是,我認爲由於post命令的異步性質,我沒有達到預期的結果。

下面是我想做的事:

for(var i = 0; i < self.images().length; i++) { 
    var photo = self.images()[i]; 
    var thisItem = photo; 
    var object = JSON.stringify({ 
    Image: thisItem.Image, 
    AlbumID: albumId, 
    Filesize: thisItem.Filesize, 
    Filetype: thisItem.Filetype, 
    Description: thisItem.Description, 
    UniqueID: thisItem.UniqueID 
    }); 
    var uri = "/api/Photo/Upload"; 
    $.post({ 
    url: uri, 
    contentType: "application/json" 
    }, object).done(function(data) { 
    if(data.IsSuccess) { 
     photo.Status(1); 
    } 
    }).fail(function() { 
    // Handle here 
    }).always(function() { 
    remainingImages = remainingImages - 1; 
    if(remainingImages == 0) self.isUploading(false); 
    }); 
} 
self.isUploading(false); 

但我覺得發生了什麼事是,所有的帖子都得到答覆之前,爲循環結束。因爲在線的一個圖像被刪除。

我嘗試了一個async: false ajax後,但鎖定屏幕,然後他們都消失了。

我認爲'完成'方法只會在帖子完成後執行,但我認爲整個方法只是在發送完帖子命令後結束,然後我永遠不會得到done

我該如何實現我想要做的事情......一旦帖子得到回覆,設置每張圖片的狀態?

+1

你的'for'循環不會等到你得到所有的承諾,因爲它會同步執行。並且,因爲(_if我沒有錯),你正在用一個變量'self.isUploading'處理圖像的上傳,它會覆蓋預期的值。我認爲會是一個好方法,就是讓每個圖像都有一個變量,以跟蹤是否上傳。然後你可以從'圖像'數組中過濾出只有那些尚未上傳的圖像... – gkb

+0

謝謝@gkb - 這就是我現在要做的。 Image對象有一個'狀態'號碼,我正在設置。 (「photo.Status(1);)」,但它似乎仍然太早完成循環。 api電話完成後,「完成」功能纔會啓動嗎?我想我明白你的意思,但不知道如何根據狀態爲0的圖像數量(0 =正在處理,1 =已上傳)來檢查上傳是否完成。 – Craig

+0

我不太清楚它是如何使用純JavaScript完成的,但是如果去圖書館看起來對你來說是可行的..你可能想在這裏查看'Q.js' - https://github.com/kriskowal/然後編寫一個函數來上傳_a單個image_,然後爲每個上傳圖像和組函數的'.then'處理程序調用此函數(如'Q.all' https://github.com/kriskowal/q#組合),你可以更新上傳圖片的狀態..... – gkb

回答

3

你的第一個問題是,你正在失去你認爲你必須每個photo對象,因爲循環完成你的AJAX調用之前返回,這樣,當他們返回,photoself.images()到最後一個項目的參考基準。

我們需要做的是解決這個問題,爲循環的每次迭代創建一個新的範圍,並且這些範圍中的每一個都有自己對特定photo的引用。 JavaScript有函數範圍,所以我們可以通過將每個photo傳遞給一個函數來實現我們的目標。我將使用一個Immediately Invoked Function Expression (IIFE)爲例:

for (var i = 0; i < self.images().length; i++) { 
    var photo = self.images()[i]; 
    (function (thisItem) { 
     /* Everything else from within your for loop goes here. */ 
     /* Note: the done handler must reference `thisItem`, not `photo`. */ 
    })(photo); 
} 

請注意,您應該從最後一行除去self.isUploading(false);,直到所有POST請求都返回後,才能將其設置爲false。

我創建了一個功能小提琴,你可以看到here

但是,此解決方案將不會按順序執行POST請求。我不確定爲什麼你希望在發送下一個POST之前等待一個POST返回,因爲這隻會增加用戶必須等待的時間。但爲了完整性,我會解釋如何去做。

要在上次POST返回後觸發POST,您需要刪除for循環。您將需要一種方法來調用上一個POST的always處理程序中的下一個POST。這是一個recursive函數的好候選。

在我的解決方案中,我使用index來跟蹤images中的哪個項目最後一次發佈。我遞歸地調用函數在images的下一個項目上執行POST,直到我們發送了images中的所有項目。下面的代碼替換for循環:

(function postNextImage (index) { 
    var photo = self.images()[i]; 
    var thisItem = photo; 
    var object = JSON.stringify({ 
     Image: thisItem.Image, 
     AlbumID: albumId, 
     Filesize: thisItem.Filesize, 
     Filetype: thisItem.Filetype, 
     Description: thisItem.Description, 
     UniqueID: thisItem.UniqueID 
    }); 

    var uri = "/api/Photo/Upload"; 

    $.post({ 
     url: uri, 
     contentType: "application/json" 
    }, object) 
    .done(function (data) { 
     if(data.IsSuccess) { 
      thisItem.Status(1); 
     } 
    }) 
    .fail(function() { 
     // Handle here 
    }) 
    .always(function() { 
     if (index < (self.images().length - 1)) { 
      index += 1; 
      postNextImage(index); 
     } else { 
      self.isUploading(false); 
     } 
    }); 

})(0); 

我創造了這個解決方案的小提琴也和可以發現here

+0

偉大的答案,我只是隻想念爲什麼你的小提琴不使用低於2.2.0的jQuery版本? – deblocker

+0

這是因爲'$ .post'的重載將'settings'對象作爲第一個參數,並且直接從您的問題中複製而來,直到jQuery 2.2才引入。請參閱:https://api.jquery.com/jquery.post/#jQuery-post-settings。我更新了第一個小提琴與jQuery <2.2兼容:https://jsfiddle.net/76484/vgsh2jh8/3/ – 76484

+0

令人難以置信的答案!我會在早上嘗試這個。非常感謝你的努力。 – Craig