业务中经常会有一些批量操作的任务,比如使用 JavaScript 预加载一组图片,批量上传一些资源。如果这些任务一次性启动,势必会消耗很多资源和带宽。理想的做法应该对这些任务进行限制,比如一次只跑几个,当其中一些任务完成后,再添加新的任务到队列。

总的来说,我们需要这样一个服务,它提供一个添加任务的方法,内部将添加的方法维护在一个数组。然后根据设置的阈值,即同时可跑的任务数,来执行这些任务。

同时为了打日志方便,注册的任务可指定一个名称,所以一个任务的类型看起来应该像这样:

type Task<T> = {
name?: string;
fn: () => Promise<T>;
};

其中 name 方便调试,fn 便是需要执行的任务,它应该是一个比较耗时的异步任务,所以调用后返回 Promise。

运行任务的服务:

export class TaskRunner {
private queue: Task<any>[] = [];
private activeTaskNum: number = 0; constructor(private limit = 5, public debug = false) {

if (limit < 1) {

throw new Error("limit must be interger greater then 1");

}

} public addTask<T>(task: Task<T>) {

task.name ? task.name : task.fn.name || this.queue.length || "";

this.queue.push(task);

this.runTask();

} private execute<T>(task: Task<T>) {

this.log(</span>running ${<span class="pl-smi">task</span>.<span class="pl-c1">name</span>}<span class="pl-pds">);

return task

.fn()

.then(ressult => {

this.log(</span>task ${<span class="pl-smi">task</span>.<span class="pl-c1">name</span>} finished<span class="pl-pds">);

return ressult;

})

.catch(error => {

this.log(</span>${<span class="pl-smi">task</span>.<span class="pl-c1">name</span>} failed<span class="pl-pds">);

throw error;

})

.finally(() => {

this.activeTaskNum--;

this.runTask();

});

} private runTask() {

while (this.activeTaskNum < this.limit && this.queue.length > 0) {

const task = this.queue.shift();

this.activeTaskNum++;

this.execute(task!);

}

} private log(msg: string) {

if (this.debug) {

console.info(</span>[TaskRunner] ${<span class="pl-smi">msg</span>}<span class="pl-pds">);

}

}

}

因为任务可以动态添加,所以在添加完任务的方法 addTask() 里就启动任务队列的执行 runTask(),无须外部显式触发。

测试上面的代码:

import { TaskRunner } from "./taskRunner";

const runner = new TaskRunner(3, true);

function taskGenerator(taskName: string, time: number) {

return {

name: taskName,

fn: () =>

new Promise<string>((resolve, _reject) => {

setTimeout(() => {

resolve(</span>result for task ${<span class="pl-smi">taskName</span>}<span class="pl-pds">);

}, time);

})

};

} const errorTask = {

name: "errroTask",

fn: () =>

new Promise<string>((_resolve, reject) => {

setTimeout(() => {

reject("errorTask failed");

}, 3000);

})

}; [errorTask]

.concat(

[...new Array(5).keys()].map((_value, index) =>

taskGenerator(String(index), Math.random() * 10000 + 1000)

)

)

.forEach(task => runner.addTask(task));

这里生成了 5 个任务,每个任务的耗时是随机的 1s ~ 10s。同时添加一个了个直接 reject 的任务来模拟任务失败时,不会影响其他任务的执行。

完整的代码移步 GitHub 仓库 wayou/task-runner

相关资源

TypeScript 实现任务队列的更多相关文章

  1. 异步任务队列Celery在Django中的使用

    前段时间在Django Web平台开发中,碰到一些请求执行的任务时间较长(几分钟),为了加快用户的响应时间,因此决定采用异步任务的方式在后台执行这些任务.在同事的指引下接触了Celery这个异步任务队 ...

  2. TypeScript: Angular 2 的秘密武器(译)

    本文整理自Dan Wahlin在ng-conf上的talk.原视频地址: https://www.youtube.com/watch?v=e3djIqAGqZo 开场白 开场白主要分为三部分: 感谢了 ...

  3. TypeScript为Zepto编写LazyLoad插件

    平时项目中使用的全部是jQuery框架,但是对于做webapp来说jQuery太过于庞大,当然你可以选择jQuery 2.*针对移动端的版本. 这里我采用移动端使用率比较多的zepto框架,他跟jqu ...

  4. TypeScript Vs2013 下提示Can not compile modules unless '--module' flag is provided

    VS在开发TypeScript程序时候,如果import了模块有的时候会有如下提示: 这种情况下,只需要对当前TypeScript项目生成设置为AMD规范即可!

  5. TypeScript

    TypeScript: Angular 2 的秘密武器(译)   本文整理自Dan Wahlin在ng-conf上的talk.原视频地址: https://www.youtube.com/watch? ...

  6. 打造TypeScript的Visual Studio Code开发环境

    打造TypeScript的Visual Studio Code开发环境 本文转自:https://zhuanlan.zhihu.com/p/21611724 作者: 2gua TypeScript是由 ...

  7. 转职成为TypeScript程序员的参考手册

    写在前面 作者并没有任何可以作为背书的履历来证明自己写作这份手册的分量. 其内容大都来自于TypeScript官方资料或者搜索引擎获得,期间掺杂少量作者的私见,并会标明. 大部分内容来自于http:/ ...

  8. 通过IEnumerable和IDisposable实现可暂停和取消的任务队列

    一般来说,软件中总会有一些长时间的操作,这类操作包括下载文件,转储数据库,或者处理复杂的运算. 一种处理做法是,在主界面上提示正在操作中,有进度条,其他部分不可用.这里带来很大的问题, 使用者不知道到 ...

  9. Webstorm编译TypeScript

    下载webstorm 下载node.js编译器npm   Webstorm的安装很简单.但如果没有Java For Mac 环境打开Webstorm时会有提示,点击提示会跳转下载链接,下载安装就好. ...

随机推荐

  1. Mac 下实现 pyenv/virtualenv 与 Anaconda 的兼容

    http://blog.csdn.net/vencent7/article/details/76849849 自己一直用的 pyenv 和 pyenv-virtualenv 管理不同的 python ...

  2. Spring MVC “404 Not Found”错误的解决

    一般这个问题发生的原因不会出在Web.xml配置文件的Servlet URL映射部分,因为这个URL映射做JavaWeb的人经常配置不会发生错误,而且此处的这个映射自由度很大,可以有目录字符串,也可以 ...

  3. Windows下MySQL重装引起问题的解决

    解决了Plugin 'InnoDB' init function returned error问题和error1405那个安全设置密码登陆问题,我个人觉得关键点在于删除C:/Documents and ...

  4. 微信公众号网页授权登录--JAVA

    网上搜资料时,网友都说官方文档太垃圾了不易看懂,如何如何的.现在个人整理了一个通俗易懂易上手的,希望可以帮助到刚接触微信接口的你. 请看流程图!看懂图,就懂了一半了: 其实整体流程大体只需三步:用户点 ...

  5. 四年级--python函数基础用法

    一.函数的定义,调用和返回值 1.1 语法 def 函数(参数一,参数二...): ''' 文档注释 ''' 代码逻辑一 代码逻辑二 .... return 返回值 1.2 定义函数的三种形式 说明: ...

  6. htmlparser 学习

    htmlparser 学习系列 htmlparser 使用法使用与详解

  7. Centos6离线安装MySQL5.5.55-1(附带安装包及Perl依赖包)

    资源包下载https://pan.baidu.com/s/1U3myYp4GSmDUfZocMWI9FA 密码:xdac 资源包所带有的资源截图 1.上传MySQL-client-5.5.55-1.l ...

  8. jq slideToggle()坑

    jQuery slideToggle() 方法 jQuery slideToggle() 方法可以在 slideDown() 与 slideUp() 方法之间进行切换. 如果元素向下滑动,则 slid ...

  9. MySQL 数据库 Query 的优化

    理解MySQL的Query Optimizer MySQL Optimizer是一个专门负责优化SELECT 语句的优化器模块,它主要的功能就是通过计算分析系统中收集的各种统计信息,为客户端请求的Qu ...

  10. SpringMVC, Spring和Mybatis整合案例一

    一  准备工作 包括:spring(包括springmvc).mybatis.mybatis-spring整合包.数据库驱动.第三方连接池. 二  整合思路 Dao层: 1.SqlMapConfig. ...