先簡單介紹一下業務場景,咱們的項目是一個微博輿情分析系統,能夠根據用戶設置的關鍵字監測相關微博輿情,並進行實時推送。監測範圍涵蓋境內和境外微博平臺(境內:新浪、騰訊,境外:twitter)。瀏覽器
由於訪問境外的微博須要通過代理轉發,致使常常有圖片加載不出來,嚴重影響用戶觀感。此時就須要一個錯誤重試的處理,不要讓界面上出現這個圖片加載錯誤的圖標,以下所示: 函數
經過外部傳進來的表明圖片來源的參數,判斷是不是境外圖片。若是是,則先顯示一張默認圖片,等圖片加載成功後再替換默認圖片。若是圖片加載錯誤了,間隔一段時間進行重試,直到加載成功,或失敗次數大於設定的最大閾值時中止。ui
圖片加載主要有三種狀態:成功、失敗、加載中(pendding),這個能夠在控制檯的NetWork裏面看到。url
先寫一個函數,裏面定義圖片加載失敗後的最大重試次數
maxCount
,初始化錯誤重試次數,並對圖片url進行一個非空驗證spa
/** * @param imageUrl {string} 圖片URL * @param callBack {function} 圖片加載成功後的回調(參數:{imageUrl}) */
function loadImage(imageUrl, callBack) {
if (!imageUrl) {
return callBack({
imageUrl: ''
});
}
const maxCount = 5; // 最大重試次數
let count = 0; // 重試次數
// 驗證圖片是否加載成功
validateImage(imageUrl, callBack, maxCount, count);
}
複製代碼
接下來就是核心代碼
validateImage
函數的實現了, 首先定義好函數內部的變量代理
function validateImage(imageUrl, callBack, maxCount, count) {
var img = new Image(), // 建立一個image對象
timeId,
timer,
finished, // 圖片加載成功標誌位
time = 40, // 觀察圖片狀態的定時器執行間隔
timeCount = 0, // 觀察圖片狀態的定時器執行次數
totalTime = 0, // 觀察圖片狀態的定時器累計執行時間
errTimeout = 1000; // 失敗重試的延時
複製代碼
而後給
img.src
賦值,此時圖片就開始加載了code
img.src = imageUrl;
count++;
複製代碼
用
img.complete
判斷圖片是否加載完畢,爲true,則表示圖片已加載成功,直接執行回調函將正確的圖片路徑返回cdn
if (img.complete) {
callBack({
imageUrl: imageUrl,
});
}
複製代碼
若是
img.complete
爲false,則須要分狀況處理。對象
狀況一:圖片加載中(pendding狀態),咱們須要定時去看圖片狀態,直到圖片加載成功或者失敗,用
onload
事件或onerror
事件去捕獲。
然而這個地方有個大坑,什麼坑呢?
圖片有可能一直處於pendding狀態,此時是不會有任何返回值的,因此沒有辦法用onload
或onerr
事件捕獲,定時器一直重複執行,內存狂飆!blog
else {
if(totalTime < 60000){
timeId = setTimeout(function () {
timer();
timeCount++;
// 每次執行間隔是上次執行間隔的兩倍
time = 40*Math.pow(2,timeCount);
totalTime += time;
}, time);
}else{
clearTimeout(timeId);
}
}
};
// 執行觀察定時器
timeId = setTimeout(function () {
timer();
totalTime += time;
}, time);
複製代碼
此時若是圖片加載成功或失敗則會被onload捕獲或onerr捕獲。圖片加載成功進入到
onload
裏面,執行回調並將正確的圖片路徑返回;圖片加載失敗,進入到onerror
裏面,並進行重試,直到成功或者重試次數大於maxCount
結束。
img.onload = function () {
clearTimeout(timeId);
if (!finished) {
return callBack({
imageUrl: imageUrl
});
}
};
img.onerror = function () {
if (count < maxCount) {
clearTimeout(timeId);
// 錯誤重試間隔每次以2s遞增
errTimeout += (count-1) * 2000;
// 遞歸
timeId = setTimeout(function () {
validateImage(imageUrl, callBack, maxCount, count);
}, errTimeout);
} else {
clearTimeout(timeId);
if (!finished) {
callBack({
imageUrl: ''
});
}
}
};
}
複製代碼
ok,大功告成!
寫的很差,僅供參考。謝謝你們
/** * 驗證圖片是否加載成功 * @param imageUrl * @param callBack * @param maxCount {string} 最大輪詢次數 * @param count {string} 從新加載次數 */
function validateImage(imageUrl, callBack, maxCount, count) {
let img = new Image(), timeId, timer, finished, time = 40, timeCount = 0, totalTime = 0, errTimeout = 1000;
img.src = imageUrl;
count++;
if (img.complete) {
callBack({
imageUrl: imageUrl,
});
} else {
timer = function () {
if (img.width > 0 || img.height > 0) {
finished = true;
callBack({
imageUrl: imageUrl
});
} else {
if(totalTime < 60000){
timeId = setTimeout(function () {
timer();
timeCount++;
time = 40*Math.pow(2,timeCount);
totalTime += time;
}, time);
}else{
clearTimeout(timeId);
}
}
};
timeId = setTimeout(function () {
timer();
totalTime += time;
}, time);
img.onload = function () {
clearTimeout(timeId);
if (!finished) {
return callBack({
imageUrl: imageUrl
});
}
};
img.onerror = function () {
if (count < maxCount) {
clearTimeout(timeId);
errTimeout += (count-1) * 2000;
timeId = setTimeout(function () {
validateImage(imageUrl, callBack, maxCount, count);
}, errTimeout);
} else {
clearTimeout(timeId);
if (!finished) {
callBack({
imageUrl: ''
});
}
}
};
}
}
/** * @param imageUrl {string} 圖片URL * @param callBack {function} 圖片加載成功後的回調(參數:{imageUrl}) * @returns {*} */
function loadImage(imageUrl, callBack) {
if (!imageUrl) {
return callBack({
imageUrl: ''
});
}
const maxCount = 5;
let count = 0;
validateImage(imageUrl, callBack, maxCount, count);
}
export default loadImage;
複製代碼