广告
返回顶部
首页 > 资讯 > 前端开发 > JavaScript >JS下大批量异步任务按顺序执行的示例分析
  • 830
分享到

JS下大批量异步任务按顺序执行的示例分析

2024-04-02 19:04:59 830人浏览 泡泡鱼
摘要

这篇文章主要介绍了js下大批量异步任务按顺序执行的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。前言最近需要做一个浏览器的, 支持大

这篇文章主要介绍了js下大批量异步任务按顺序执行的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。

前言

最近需要做一个浏览器的, 支持大体积文件上传且要支持断点续传的上传组件, 本来以为很容易的事情, 结果碰到了一个有意思的问题:

循环执行连续的异步任务, 且后一个任务需要等待前一个任务的执行状态

这么说可能有点空泛, 以我做的组件举例:

这个组件本意是为了上传大体积视频, 和支持断点续传, 因为动辄几个G的视频不可能直接把文件读进内存, 只能分片发送(考虑到实际网络状态, 每次发送大小定在了4MB), 而且这么做也符合断点续传的思路.

组件工作流程如下:

  1. 选定上传文件后, 从H5原生upload组件里取得文件的blob对象  (同步)

  2. 通过blob对象的slice方法把文件切片  (同步)

  3. 新建一个Filereader对象, 通过Filereader的readAsArrayBuffer方法读取步骤2中生成的slice  (异步)

  4. 如果步骤3的buffer读取成功(通过监控Filereader的onload事件), 则ajax发送步骤3中的buffer  (异步)

  5. 如果ajax发送成功, 且服务器储存完成, 会向客户端发回一个成功状态码, 如果ajax的response中存在这个状态码, 则进行下一次切片发送  (异步)

从组件工作流程可以发现, 3,4,5中的连续异步任务, 必须要按顺序进行, 且每一步任务间存在相互依赖, 最后还要对这些步骤进行多次循环.

如果只是处理单次的连续异步任务, 通过promise链式调用即可, 但是要循环执行这样的连续异步任务让我想了很久.

后来Google了很久也没发现解决方案, 无奈下闭门造车了2天, 想出了3套方案, 权当抛砖引玉, 希望各位给出更好建议

3套方案的核心思想相同, 类似观察者模式, 来控制循环的进行, 区别在于循环的实现不同, 实际上这3套方案也是我自我否定的过程, 不断思考更好的方法, 整个组件代码略长, 在此只挑出问题相关部分, 且省略错误处理部分

方案1

依然以上传组件举例

//循环状态标记,0为初始状态,1为正常,2为出错
let status = 0;


const createReader = ()=> {
 return new Promise ((reslove, reject)=> {
  let reader = new Filereader();
  ...
  reader.onload = ()=> {
   reslove(reader.result)
  }
  reader.onerror = ()=> reject()
 })
}

// ajax发送createReader方法读取到的Buff
const createXhr = ()=> {
 const xhr= new XMLHttpRequest();
 return new Promise ((reslove, reject)=> {
  ...
  xhr.onreadystatechange= ()=> {
   ...
   //如果readyState == 4,status == 200且服务器的状态码存在,更改全局标记为1
   status = 1;
   reslove()
  }
 })
}

//每一轮循环开始前都检查一次全局状态标记
const checkStatus = ()=> {
 ...
 if (status == 1) {
  loop()
 }
}

//循环过程的链式调用
const loop = ()=> {
 createReader().then(()=> createXhr()).then(()=> checkStatus());
}

方案1是基于初见问题的'想当然'解决方法, 碰到异步任务就promise, 这样的循环长链调用, 写法不优雅, 且错误调试异常麻烦, 更爆炸的是因为闭包问题, 在循环执行中这些内存难以回收, 内存消耗急剧增加, 只能等待循环执行完成

方案2

彻底引入观察者模式, 构造一个简单的EventEmitter, 通过event.on, event.emit的形式完成循环

//模仿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();
...
//监听createReader事件,如果读取buffer成功就触发toajax事件来上传切片
ev.on('createReader', ()=> {
 let reader = new Filereader();
 ...
 reader.onload = ()=> {
  ev.emit('toajax')
 }
})

//监听toajax事件,如果上传成功,就触发createReader事件开始读取下一切片
ev.on('toajax', ()=> {
 let xhr= new XMLHttpRequest();
 ...
 xhr.onreadystatechange = ()=> {
 //如果readyState == 4,status == 200且服务器的状态码存在
  ev.emit('createReader')
 }
})

方案2彻底贯彻'事件', 代码语义更自然, 错误调试也比方案1更为简单, 但内存泄漏问题依然存在

方案3

方案3, 回归方案1的状态管理方式, 但是通过setInterval方法来实现循环.

//全局状态标记
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且服务器的状态码存在
  status = 2
 }
}


let timer = setInterval(()=> {
 if (status == 2) {
  createReader();
 } else if (status == 1) {
  createXhr();
 } else if (status == 3) {
  clearInterval(timer);
 }
},10)

不可否认, 方案3看上去很low, 如果追求极致的执行效率, 方案3无疑是最蠢的办法, 但是方案三相当于把异步任务转化为了同步任务, 语义简洁, 且没有上面2种方法的内存泄漏问题.

方案3本质上是把while (true)改写成了setInterval, 因为while true会阻塞线程, 各种异步事件的回调也会被一同阻塞, 所以选择了setInterval

感谢你能够认真阅读完这篇文章,希望小编分享的“JS下大批量异步任务按顺序执行的示例分析”这篇文章对大家有帮助,同时也希望大家多多支持编程网,关注编程网JavaScript频道,更多相关知识等着你来学习!

--结束END--

本文标题: JS下大批量异步任务按顺序执行的示例分析

本文链接: https://www.lsjlt.com/news/76895.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • JS下大批量异步任务按顺序执行的示例分析
    这篇文章主要介绍了JS下大批量异步任务按顺序执行的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。前言最近需要做一个浏览器的, 支持大...
    99+
    2022-10-19
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作