欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

 更新時間:2019年01月08日 14:33:57   作者:終極蛇皮可達鴨  
這篇文章主要介紹了淺談關(guān)于JS下大批量異步任務(wù)按順序執(zhí)行解決方案一點思考,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

前言

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

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

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

這個組件本意是為了上傳大體積視頻, 和支持斷點續(xù)傳, 因為動輒幾個G的視頻不可能直接把文件讀進內(nèi)存, 只能分片發(fā)送(考慮到實際網(wǎng)絡(luò)狀態(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ā)送成功, 且服務(wù)器儲存完成, 會向客戶端發(fā)回一個成功狀態(tài)碼, 如果ajax的response中存在這個狀態(tài)碼, 則進行下一次切片發(fā)送  (異步)

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

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

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

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

方案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且服務(wù)器的狀態(tài)碼存在,更改全局標記為1
   status = 1;
   reslove()
  }
 })
}

//每一輪循環(huán)開始前都檢查一次全局狀態(tài)標記
const checkStatus = ()=> {
 ...
 if (status == 1) {
  loop()
 }
}

//循環(huán)過程的鏈式調(diào)用
const loop = ()=> {
 createReader().then(()=> createXhr()).then(()=> checkStatus());
}

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

方案2

徹底引入觀察者模式, 構(gòu)造一個簡單的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且服務(wù)器的狀態(tài)碼存在
  ev.emit('createReader')
 }
})

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

方案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且服務(wù)器的狀態(tài)碼存在
  status = 2
 }
}

/* 設(shè)置一個間隔時間極短的計時器,根據(jù)status決定下一步的任務(wù),
* 上傳完成后定時器自動清除自己
* 另外有判斷文件是否上傳完成的方法,這里就不寫了
*/
let timer = setInterval(()=> {
 if (status == 2) {
  createReader();
 } else if (status == 1) {
  createXhr();
 } else if (status == 3) {
  clearInterval(timer);
 }
},10)

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

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

總結(jié)

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

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

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

以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • JS實現(xiàn)隱藏同級元素后只顯示JS文件內(nèi)容的方法

    JS實現(xiàn)隱藏同級元素后只顯示JS文件內(nèi)容的方法

    這篇文章主要介紹了JS實現(xiàn)隱藏同級元素后只顯示JS文件內(nèi)容的方法,可實現(xiàn)將與js文件的同級元素全部隱藏,只顯示js文件內(nèi)容的功能,涉及javascript針對頁面元素的遍歷與屬性修改相關(guān)技巧,需要的朋友可以參考下
    2016-09-09
  • 手機安裝GreasyFork油猴js腳本的教程

    手機安裝GreasyFork油猴js腳本的教程

    Iceraven瀏覽器需要安裝Tampermonkey插件來安裝GF油猴腳本,本文給大家介紹手機安裝GreasyFork油猴js腳本的教程,安裝過程給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2021-12-12
  • layer實現(xiàn)彈窗提交信息

    layer實現(xiàn)彈窗提交信息

    這篇文章主要為大家詳細介紹了layer實現(xiàn)彈窗提交信息的相關(guān)代碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-12-12
  • Javascript中的幾種URL編碼方法比較

    Javascript中的幾種URL編碼方法比較

    這篇文章主要介紹了Javascript中的幾種URL編碼方法比較,本文對比了escape()、encodeURI()以及encodeURIComponent()這3種URL編碼方法,需要的朋友可以參考下
    2015-01-01
  • JS實現(xiàn)控制文本框的內(nèi)容

    JS實現(xiàn)控制文本框的內(nèi)容

    下面小編就為大家?guī)硪黄狫S實現(xiàn)控制文本框的內(nèi)容。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-07-07
  • JavaScript怎樣在刪除前添加確認彈出框?

    JavaScript怎樣在刪除前添加確認彈出框?

    這篇文章主要介紹了怎樣在刪除前添加確認彈出框?下面小編帶大家來學習一下
    2019-05-05
  • js實現(xiàn)文字頭像的生成代碼

    js實現(xiàn)文字頭像的生成代碼

    這篇文章主要介紹了js實現(xiàn)文字頭像的生成的代碼,代碼簡單易懂,非常不錯,對大家的工作或?qū)W習具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-03-03
  • 一看就懂的i++和++i示例代碼詳解

    一看就懂的i++和++i示例代碼詳解

    這篇文章主要介紹了i++和++i區(qū)別詳解,本文通過示例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-03-03
  • Javascript中的對象屬性是有序的嗎

    Javascript中的對象屬性是有序的嗎

    這篇文章主要介紹了Javascript中的對象屬性是有序的嗎,文章圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下
    2022-08-08

最新評論