<span id="mktg5"></span>

<i id="mktg5"><meter id="mktg5"></meter></i>

        <label id="mktg5"><meter id="mktg5"></meter></label>
        最新文章專題視頻專題問答1問答10問答100問答1000問答2000關鍵字專題1關鍵字專題50關鍵字專題500關鍵字專題1500TAG最新視頻文章推薦1 推薦3 推薦5 推薦7 推薦9 推薦11 推薦13 推薦15 推薦17 推薦19 推薦21 推薦23 推薦25 推薦27 推薦29 推薦31 推薦33 推薦35 推薦37視頻文章20視頻文章30視頻文章40視頻文章50視頻文章60 視頻文章70視頻文章80視頻文章90視頻文章100視頻文章120視頻文章140 視頻2關鍵字專題關鍵字專題tag2tag3文章專題文章專題2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章專題3
        問答文章1 問答文章501 問答文章1001 問答文章1501 問答文章2001 問答文章2501 問答文章3001 問答文章3501 問答文章4001 問答文章4501 問答文章5001 問答文章5501 問答文章6001 問答文章6501 問答文章7001 問答文章7501 問答文章8001 問答文章8501 問答文章9001 問答文章9501
        當前位置: 首頁 - 科技 - 知識百科 - 正文

        淺談關于JS下大批量異步任務按順序執(zhí)行解決方案一點思考

        來源:懂視網 責編:小采 時間:2020-11-27 22:02:12
        文檔

        淺談關于JS下大批量異步任務按順序執(zhí)行解決方案一點思考

        淺談關于JS下大批量異步任務按順序執(zhí)行解決方案一點思考:前言 最近需要做一個瀏覽器的, 支持大體積文件上傳且要支持斷點續(xù)傳的上傳組件, 本來以為很容易的事情, 結果碰到了一個有意思的問題: 循環(huán)執(zhí)行連續(xù)的異步任務, 且后一個任務需要等待前一個任務的執(zhí)行狀態(tài) 這么說可能有點空泛, 以我做的組件舉例: 這個
        推薦度:
        導讀淺談關于JS下大批量異步任務按順序執(zhí)行解決方案一點思考:前言 最近需要做一個瀏覽器的, 支持大體積文件上傳且要支持斷點續(xù)傳的上傳組件, 本來以為很容易的事情, 結果碰到了一個有意思的問題: 循環(huán)執(zhí)行連續(xù)的異步任務, 且后一個任務需要等待前一個任務的執(zhí)行狀態(tài) 這么說可能有點空泛, 以我做的組件舉例: 這個

        前言

        最近需要做一個瀏覽器的, 支持大體積文件上傳且要支持斷點續(xù)傳的上傳組件, 本來以為很容易的事情, 結果碰到了一個有意思的問題:

        循環(huán)執(zhí)行連續(xù)的異步任務, 且后一個任務需要等待前一個任務的執(zhí)行狀態(tài)

        這么說可能有點空泛, 以我做的組件舉例:

        這個組件本意是為了上傳大體積視頻, 和支持斷點續(xù)傳, 因為動輒幾個G的視頻不可能直接把文件讀進內存, 只能分片發(fā)送(考慮到實際網絡狀態(tài), 每次發(fā)送大小定在了4MB), 而且這么做也符合斷點續(xù)傳的思路.

        組件工作流程如下:

        1. 選定上傳文件后, 從H5原生upload組件里取得文件的blob對象  (同步)
        2. 通過blob對象的slice方法把文件切片  (同步)
        3. 新建一個Filereader對象, 通過Filereader的readAsArrayBuffer方法讀取步驟2中生成的slice  (異步)
        4. 如果步驟3的buffer讀取成功(通過監(jiān)控Filereader的onload事件), 則ajax發(fā)送步驟3中的buffer  (異步)
        5. 如果ajax發(fā)送成功, 且服務器儲存完成, 會向客戶端發(fā)回一個成功狀態(tài)碼, 如果ajax的response中存在這個狀態(tài)碼, 則進行下一次切片發(fā)送  (異步)

        從組件工作流程可以發(fā)現(xiàn), 3,4,5中的連續(xù)異步任務, 必須要按順序進行, 且每一步任務間存在相互依賴, 最后還要對這些步驟進行多次循環(huán).

        如果只是處理單次的連續(xù)異步任務, 通過promise鏈式調用即可, 但是要循環(huán)執(zhí)行這樣的連續(xù)異步任務讓我想了很久.

        后來google了很久也沒發(fā)現(xiàn)解決方案, 無奈下閉門造車了2天, 想出了3套方案, 權當拋磚引玉, 希望各位給出更好建議

        3套方案的核心思想相同, 類似觀察者模式, 來控制循環(huán)的進行, 區(qū)別在于循環(huán)的實現(xiàn)不同, 實際上這3套方案也是我自我否定的過程, 不斷思考更好的方法, 整個組件代碼略長, 在此只挑出問題相關部分, 且省略錯誤處理部分

        方案1

        依然以上傳組件舉例

        //循環(huán)狀態(tài)標記,0為初始狀態(tài),1為正常,2為出錯
        let status = 0;
        
        /* 新建Filereader,讀取文件切片,返回一個promise
        * 把讀取成功的arraybuffer通過reslove傳出
        */
        const createReader = ()=> {
         return new Promise ((reslove, reject)=> {
         let reader = new Filereader();
         ...
         reader.onload = ()=> {
         reslove(reader.result)
         }
         reader.onerror = ()=> reject()
         })
        }
        
        // ajax發(fā)送createReader方法讀取到的Buff
        const createXhr = ()=> {
         const xhr= new XMLHttpRequest();
         return new Promise ((reslove, reject)=> {
         ...
         xhr.onreadystatechange= ()=> {
         ...
         //如果readyState == 4,status == 200且服務器的狀態(tài)碼存在,更改全局標記為1
         status = 1;
         reslove()
         }
         })
        }
        
        //每一輪循環(huán)開始前都檢查一次全局狀態(tài)標記
        const checkStatus = ()=> {
         ...
         if (status == 1) {
         loop()
         }
        }
        
        //循環(huán)過程的鏈式調用
        const loop = ()=> {
         createReader().then(()=> createXhr()).then(()=> checkStatus());
        }

        方案1是基于初見問題的'想當然'解決方法, 碰到異步任務就promise, 這樣的循環(huán)長鏈調用, 寫法不優(yōu)雅, 且錯誤調試異常麻煩, 更爆炸的是因為閉包問題, 在循環(huán)執(zhí)行中這些內存難以回收, 內存消耗急劇增加, 只能等待循環(huán)執(zhí)行完成

        方案2

        徹底引入觀察者模式, 構造一個簡單的EventEmitter, 通過event.on, event.emit的形式完成循環(huán)

        //模仿node.js的EventEmitter
        class EventEmitter {
         constructor() {
         this.handler = {};
         }
         on(eventName, callback) {
         if (!this.handles){
         this.handles = {};
         }
         if (!this.handles[eventName]) {
         this.handles[eventName] = [];
         }
         this.handles[eventName].push(callback);
         }
         emit(eventName,...arg) {
         if (this.handles[eventName]) {
         for (var i=0;i<this.handles[eventName].length;i++) {
         this.handles[eventName][i](...arg);
         }
         }
         }
         }
        
        let ev= new EventEmitter();
        ...
        //監(jiān)聽createReader事件,如果讀取buffer成功就觸發(fā)toajax事件來上傳切片
        ev.on('createReader', ()=> {
         let reader = new Filereader();
         ...
         reader.onload = ()=> {
         ev.emit('toajax')
         }
        })
        
        //監(jiān)聽toajax事件,如果上傳成功,就觸發(fā)createReader事件開始讀取下一切片
        ev.on('toajax', ()=> {
         let xhr= new XMLHttpRequest();
         ...
         xhr.onreadystatechange = ()=> {
         //如果readyState == 4,status == 200且服務器的狀態(tài)碼存在
         ev.emit('createReader')
         }
        })
        
        

        方案2徹底貫徹'事件', 代碼語義更自然, 錯誤調試也比方案1更為簡單, 但內存泄漏問題依然存在

        方案3

        方案3, 回歸方案1的狀態(tài)管理方式, 但是通過setInterval方法來實現(xiàn)循環(huán).

        //全局狀態(tài)標記
        let status = 0;
        
        //讀取切片
        const createReader = ()=> {
         let reader = new Filereader();
         ...
         reader.onload = ()=>status = 1
        }
        
        //上傳切片
        const createXhr = ()=> {
         let xhr= new XMLHttpRequest();
         ...
         xhr.onreadystatechange = ()=> {
         ...
         //如果readyState == 4,status == 200且服務器的狀態(tài)碼存在
         status = 2
         }
        }
        
        /* 設置一個間隔時間極短的計時器,根據status決定下一步的任務,
        * 上傳完成后定時器自動清除自己
        * 另外有判斷文件是否上傳完成的方法,這里就不寫了
        */
        let timer = setInterval(()=> {
         if (status == 2) {
         createReader();
         } else if (status == 1) {
         createXhr();
         } else if (status == 3) {
         clearInterval(timer);
         }
        },10)
        
        

        不可否認, 方案3看上去很low, 如果追求極致的執(zhí)行效率, 方案3無疑是最蠢的辦法, 但是方案三相當于把異步任務轉化為了同步任務, 語義簡潔, 且沒有上面2種方法的內存泄漏問題.

        方案3本質上是把while (true)改寫成了setInterval, 因為while true會阻塞線程, 各種異步事件的回調也會被一同阻塞, 所以選擇了setInterval

        總結

        當時還嘗試過使用Object.defineProperty方法給status 綁一個set方法, 通過每次給status set新值的時候來判斷循環(huán), 但是發(fā)現(xiàn)這樣做依然像是鏈式調用, 一樣存在內存泄漏問題, 這里就不寫了.

        說實話, 這3個方案感覺都有很大缺陷, 甚至可以說粗淺, 本人入坑前端2個月, 眼界有限無可避免, google無門后, 想到社區(qū)來求助, 希望老哥們提供更好的思路.

        最后掛上文中提到的上傳插件, 因為感覺還有缺陷就沒封裝, 只做了個demo(前端上傳插件用的方案2, 后端拼接文件切片用的方案3)

        聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯(lián)系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

        文檔

        淺談關于JS下大批量異步任務按順序執(zhí)行解決方案一點思考

        淺談關于JS下大批量異步任務按順序執(zhí)行解決方案一點思考:前言 最近需要做一個瀏覽器的, 支持大體積文件上傳且要支持斷點續(xù)傳的上傳組件, 本來以為很容易的事情, 結果碰到了一個有意思的問題: 循環(huán)執(zhí)行連續(xù)的異步任務, 且后一個任務需要等待前一個任務的執(zhí)行狀態(tài) 這么說可能有點空泛, 以我做的組件舉例: 這個
        推薦度:
        • 熱門焦點

        最新推薦

        猜你喜歡

        熱門推薦

        專題
        Top
        主站蜘蛛池模板: 一本色道久久综合亚洲精品蜜桃冫| 激情内射亚洲一区二区三区| 亚洲色精品三区二区一区| 久久午夜夜伦鲁鲁片免费无码影视| 亚洲高清国产AV拍精品青青草原| 免费无码又爽又刺激网站| 亚洲av最新在线网址| 亚洲成人免费在线| 亚洲国产精品成人综合久久久 | 含羞草国产亚洲精品岁国产精品 | 国产亚洲精品欧洲在线观看| 国产免费私拍一区二区三区| 深夜a级毛片免费视频| 亚洲一区二区三区乱码A| 中文字幕在线视频免费| 日产亚洲一区二区三区| 无人影院手机版在线观看免费| 亚洲性色精品一区二区在线| 免费一级毛片不卡不收费| 一级毛片**免费看试看20分钟| 亚洲国产另类久久久精品| 国产免费不卡视频| 亚洲国产精品网站在线播放| 亚洲国产精品第一区二区三区 | 91在线老王精品免费播放| 亚洲一区欧洲一区| www国产亚洲精品久久久| 在线人成免费视频69国产| 久久亚洲国产成人影院| 亚洲日本中文字幕天堂网| 99在线观看免费视频| 亚洲欧洲日韩极速播放| 亚洲日韩国产成网在线观看| 97在线视频免费播放| 国产亚洲综合久久| 亚洲爱情岛论坛永久| 国产高清免费的视频| 无码国产精品一区二区免费vr| 男人的天堂av亚洲一区2区| 亚洲VA中文字幕不卡无码| 好爽…又高潮了毛片免费看 |