說明:寫這篇文章,是但願被吐槽的。ajax
1、背景promise
在作報表頁面的時候,頁面上有不少的異步加載,而設計的loading是個全局的,一個頁面就有一個。異步
控制loading何時出現,何時消失,要實時的知道頁面上異步加載的東西是否執行完畢,只有全部的異步都加載完,loading才能中止。ide
而且,若是用戶操做了頁面,某個局部又要開始加載,loading要被通知到,執行loading效果。函數
問題的難點有兩個:this
1.怎麼知道全部的異步都加載完閉了呢?spa
2.如何通知loading?prototype
2、思路設計
1.思路一code
設計一個外部狀態map,異步執行完畢,就修改外部變量的值。開一個計時器,不斷的這個map,若是所有加載完畢,就中止loading。
// 狀態集合 var statusMap = { "異步1": false, "異步2": false // ... }; // 計時器不斷檢查狀態 setInterval(function () { // 檢查statusMap if (isReady(statusMap)) { // show loading } else { // hide loading } }, 1000); //異步 $.ajax({ //... success: function () { statusMap["異步1"] = true; } });
這個方案雖然能解決問題,可是太不夠優雅,呵呵呵呵......
2.思路二
使用promise或者jQuery的deferred(@上位者的憐憫 提供的建議)。
$.when(異步1,異步2).done(function(){ // show loading }).fail(function(){ // hide loading });
這個方案有個弊端,一旦異步加載完畢,不能修改promise的狀態了,所以不適合這個場景。
3.思路三
本身造輪子基於事件監聽的,一個狀態改變,就掃一遍全部監聽的對象,最後再根據用狀態,回調對應的函數
3、思路三實現
(function (win) { var watch = function () { var that = this; // arr if (arguments.length == 1 && Object.prototype.toString.call(arguments[0]) === "[object Array]") { that.watchArr = arguments[0]; } else { that.watchArr = Array.prototype.slice.call(arguments); } that.watchArr.forEach(function (fairy) { fairy.watch = that; }); that.isReady = true; return that; }; watch.prototype = { ready: function (callback) { this.readyHandler = callback; this.check(); return this; }, unready: function (callback) { this.unReadyHandler = callback; this.check(); return this; }, add: function (fairy, check) { this.watchArr.push(fairy); if (check === undefined || check === true) { this.check(); } return this; }, check: function () { var nowIsReady = this.watchArr.every(function (fairy) { return fairy.isReady; }); if (nowIsReady != this.isReady) { if (nowIsReady) { if (this.readyHandler) { this.readyHandler.call(); this.isReady = true; } } else { if (this.unReadyHandler) { this.unReadyHandler.call(); this.isReady = false; } } } }, setIsReadyById: function (id, isReady, check) { var index = this.watchArr.indexOf(function (fairy) { fairy.id = id; }); this.watchArr[index].isReady = isReady; if (check === undefined || check === true) { this.check(); } }, setIsReadyAll: function (isReady, check) { this.watchArr.forEach(function (fairy) { fairy.isReady = isReady; }); if (check === undefined || check === true) { this.check(); } } }; var fairy = function (isReady) { this.id = new Date().getTime(); this.isReady = isReady || false; }; fairy.prototype = { toReady: function () { this.isReady = true; this.watch.check(); }, toUnReady: function () { this.isReady = false; this.watch.check(); } }; win.Watch = watch; win.Fairy = fairy; })(window);
// 用例 var loader = new Loader(); var listFairy = new Fairy(); var chartFairy = new Fairy(); var watch = new Watch(listFairy, chartFairy); watch.ready(function () { loader.stop(); }).unready(function () { loader.start(); }); //異步 $.ajax({ //... success: function () { listFairy.toReady(); } });
納尼,說好的事件呢,哪裏去了。
好吧,吐槽點來了,實現的過程當中,發現其實不必用事件。
實現到最後,發現它其實就是一個變相的思路一。
the end 未完不續 ...