thenjs

Another very small asynchronous promise tool!

npm install thenjs
6 downloads in the last day
60 downloads in the last week
269 downloads in the last month

then.js 0.12.1Build Status

小巧、简单、强大的链式异步编程工具!

能用简单优美的方式将任何同步或异步回调函数转换成then()链式调用!

你可以在服务器端(node.js)或浏览器中使用then.js,兼容ie6/7/8。

特征

  1. 无需像Q.js那样封装,可以用自然的方式直接把N多异步回调函数写成一个长长的then链;
  2. 拥有类似Async.js但更强大的each、eachSeries、parallel、series批量异步组合函数,它们都可在then链上调用;
  3. Error收集器fail方法可在任意位置调用,可以调用一次或多次,让你随心所欲处理各种Error。还可以把fail放在末尾当作殿后函数运行(即不管then链成功或失败均运行该函数);
  4. 开启debug模式,可以把每一个then链运行结果输出到debug函数(未定义debug函数则console.log);
  5. 全局错误处理onerror;
  6. then链续接,当一条then链的所有任务运行完毕之后,还可以在其后继续追加then链,前面运行的结果能进入续接的then链继续处理。

Benchmark

each、eachSeries、parallel、series长度10000各跑1000次node test/benchmark.js

异步任务,thenjs 略胜:

async : 1000 loops, 23.853 ms/loop, 41.92 ops/sec. thenjs : 1000 loops, 22.718 ms/loop, 44.02 ops/sec.

同步任务各有胜负,thenjs 内部将同步任务转成了异步, async 不支持过长(如超过3000)的同步任务(将会出现Maximum call stack size exceeded

Install

Node.js:

npm install thenjs

bower:

bower install thenjs

Browser:

<script src="/pathTo/then.js"></script>

with require

var then = require('thenjs');

with define

define(['thenjs'], function (then) {
    //...
});

API

概览

入口函数:(return then对象)

  • then([startHandler], [debug])

  • then.each(array, iterator, [debug])

  • then.eachSeries(array, iterator, [debug])

  • then.parallel(taskFnArray, [debug])

  • then.series(taskFnArray, [debug])

  • then.onerror(errorHandler) (无返回)

  • then.nextTick(fn) (无返回)

then对象方法:(return then对象)

  • .then(successHandler, [errorHandler])

  • .all(allHandler)

  • .fail(errorHandler)

  • .each(array, iterator)

  • .eachSeries(array, iterator)

  • .parallel(taskFnArray)

  • .series(taskFnArray)

1. 入口函数

then([startHandler], [debug])

startHandler是可选的,如果未提供,将直接进入下一个then object。

Parameters:

  • startHander: function (defer) {}
  • context: 为startHandler绑定的this值,下同
  • debug: debug为函数或其它真值时就对本then链开启调试模式,逐步将每一个链的defer运行结果用debug函数处理,如果debug为非函数真值,则调用console.log打印,下同

Return: then object

then.each(array, iterator, [debug])

将array中的值应用于iterator函数(同步或异步任务),并行执行。iterator的第一个参数defer用于收集err和运行结果,所有结果将形成一个结果数组进入下一个then object,结果数组的顺序与array对应。当所有iterator任务运行完毕,或者defer捕捉到任何一个err,即进入下一个then object。如果array为空数组,结果数组也将为空数组,iterator不会执行而直接进入下一个then object。

Parameters:

  • array: array apply to iterator
  • taskIterator: function (defer, value, index, array) {}
  • context: context apply to iterator

Return: then object

then.eachSeries(array, iterator, [debug])

将array中的值应用于iterator函数(同步或异步任务),按顺序执行,上一个任务执行完毕才开始执行下一个任务。iterator的第一个参数defer用于收集err和运行结果,所有结果将形成一个结果数组进入下一个then object,结果数组的顺序与array对应。当所有iterator任务运行完毕,或者defer捕捉到任何一个err,即进入下一个then object。如果array为空数组,结果数组也将为空数组,iterator不会执行而直接进入下一个then object。

Parameters:

  • array: array apply to iterator
  • taskIterator: function (defer, value, index, array) {}
  • context: context apply to iterator

Return: then object

then.parallel(taskFnArray, [debug])

taskFnArray是一系列同步或异步任务函数组成的数组,并行执行。taskFnArray中每一个函数的第一个参数defer用于收集err和运行结果,所有结果将形成一个结果数组进入下一个then object,结果数组的顺序与taskFnArray对应。当所有taskFnArray任务运行完毕,或者defer捕捉了任何一个err,即进入下一个then object。如果taskFnArray为空数组,结果数组也将为空数组,将会直接进入下一个then object。

Parameters:

  • taskFnArray: [taskFn1, taskFn2, taskFn3, ...]
  • taskFn in taskFnArray: function (defer) {}
  • context: context apply to iterator

Return: then object

then.series(taskFnArray, [debug])

taskFnArray是一系列同步或异步任务函数组成的数组,按顺序执行,上一个任务执行完毕才开始执行下一个任务。taskFnArray中每一个函数的有第一个参数defer用于收集err和运行结果,所有结果将形成一个结果数组进入下一个then object,结果数组的顺序与taskFnArray对应。当所有taskFnArray任务运行完毕,或者defer捕捉了任何一个err,即进入下一个then object。如果taskFnArray为空数组,结果数组也将为空数组,将会直接进入下一个then object。

Parameters:

  • taskFnArray: [taskFn1, taskFn2, taskFn3, ...]
  • taskFn in taskFnArray: function (defer) {}
  • context: context apply to iterator

Return: then object

2. then object的方法

.all(allHandler)

若all存在,则上一个then对象无论是捕捉到err还是正常结果,均进入all执行,allHandler可用上层then对象的defer代替.all(defer)

Parameters:

  • allHandler: function (defer, err, value…) {}

Return: then object

.then(successHandler, [errorHandler])

若errorHandler存在,上一个then对象捕捉到err则执行errorHandler,否则执行successHandler, errorHandler可用上层then对象的defer代替,successHandler则不能(否则会把第一个value当作err处理)。

Parameters:

  • successHandler: function (defer, value…) {}
  • errorHandler: function (defer, err) {}

Return: then object

.fail(errorHandler)

fail用于捕捉在它之前的then链上发生的任何err。若fail存在,fail之前的then链发生了err,且没有被all的allHandler或then的errorHandler捕捉,则err直接进入最近的fail节点,err发生点与fail之间的then链不会被执行。errorHandler可用上层then对象的defer代替.fail(defer)。一个then链可存在0个或多个fail方法,强烈建议then链的最后一个then对象为fail方法。如果then链中没有任何err捕捉器,则err会直接throw。fail方法可用于运行殿后函数:对一个一条then链,使用defer(true)把最后无err运行的then对象导向末端的fail,则该then链不管是正确运行还是发生了err,均会执行末端的fail方法。

Parameters:

  • errorHandler: function (defer, err) {}

Return: then object

.each(array, iterator)

参数类似then.each。不同在于,一是无debug参数; 当array, iterator, context为null或undefined时,此处的each会查找上一个then对象的输出结果,如果存在,则作为它的运行参数:例如上一个then对象输出defer(null, array1, iterator1),此处为each(null, null, this),则该each会获取array1, iterator1作为为它的参数运行。

.eachSeries(array, iterator)

参数类似then.eachSeries。不同处类似上面.each。

.parallel(taskFnArray)

参数类似then.eachSeries。不同处类似上面.each。

.series(taskFnArray)

参数类似then.eachSeries。不同处类似上面.each。

3. 其他说明

  1. 关于Error收集器

    then对象的then方法的errorHandler函数、all方法、fail方法均能收集error。其中then方法的errorHandler函数和all方法只能收集上一个then对象产生的error;fail方法则能收集再它之前所有then链产生的error。

  2. 关于触发器defer

    then.js中最关键的就是defer,用于触发下一个then链。从上面可知,入口函数、then方法、all方法、fail方法中的任务函数的第一个参数都被注入了defer方法,如果任务函数本身是一个defer方法,则不会再被注入defer方法

    defer的第一个参数永远是error,如果error存在,则error下一个then对象的Error收集器,如果Error收集器不存在,则抛出error。

    如果异步任务的callback的第一个参数为error,即callback(error, result1, ...)的形式,则可直接用defer代替异步任务的callback。Node.js中的异步函数基本都是这种形式,then.js用起来超方便。

    如果一个函数体内的同一个defer被多次调用,那么只有最先被触发的那个defer有效。这个效果类似于or:多个异步任务同时进行,最先返回的结果进入下一个then链,其它后返回的结果忽略。

  3. 关于fail方法

    fail方法能捕捉在它之前的then链中的任何一个error。fail的优先级低于then方法的errorHandler和all方法,即then对象不存在then方法的errorHandler和all方法时error才会进入fail。当then链的某个then对象产生了error时,如果该then对象的下一个then对象存在Error收集器,则error进入该Error收集器,否则error会直接进入then链下游最近的fail方法,其间的then对象均会跳过。

then.js使用模式

直链:

then(function (defer) {
    // ....
    defer(err, ...);
}).then(function (defer, value) {
    // ....
    defer(err, ...);
}, function (defer, err) {
    // ....
    defer(err, ...);
}).then(function (defer) {
    // ....
    defer(err, ...);
}).all(function (defer, err, value) {
    // ....
    defer(err, array);
}).each(null, function (defer, value) {
    // ....
    defer(err, ...);
}).eachSeries(null, function (defer) {
    // ....
    defer(err, ...);
}).parallel(null, function (defer) {
    // ....
    defer(err, ...);
}).series(null, function (defer) {
    // ....
    defer(err, ...);
}).fail(function (defer, err) {
    // ....
});

嵌套:

then(function (defer) {
    // ....
    defer(err, ...);
}).then(function (defer, value) {
    //第二层
    then(function (defer2) {
        // ....
        defer2(err, ...);
    }).then(function (defer2, value) {
        //第三层
        then(function (defer3) {
            // ....
        }).all(defer2); // 返回二层
    }).then(function (defer2) {
        // ....
        defer(err, ...); // 返回一层
    }).fail(defer); // 返回一层
}).then(function (defer) {
    // ....
    defer(err, ...);
}).fail(function (defer, err) {
    // ....
});

async 嵌套:

then(function (defer) {
    // ....
    defer(err, array);
}).then(function (defer, array) {
    // ....并行执行任务
    then.each(array, function (defer2, value) {
        defer2();
    }, defer);
}).then(function (defer, array) {
    // ....逐步执行任务
    then.eachSeries(array, function (defer2, value) {
        defer2();
    }, defer);
}).then(function (defer, array) {
    // ....并行执行任务
    then.parallel([function (defer2) {
        //任务1
        defer2();
    }, function (defer2) {
        //任务2
        defer2();
    }, function (defer2) {
        //任务3
        defer2();
    }, ...], defer);
}).then(function (defer, array) {
    // ....逐步执行任务
    then.series([function (defer2) {
        //任务1
        defer2();
    }, function (defer2) {
        //任务2
        defer2();
    }, function (defer2) {
        //任务3
        defer2();
    }, ...], defer);
}).then(function (defer) {
    // ....
    defer(err, ...);
}).fail(function (defer, err) {
    // ....
});

then对象取代callback:

function getFileAsync() {
    return then(function (defer) {
        readFile(failname, defer);
    }).then(function (defer, fileContent) {
        // 处理fileContent
        defer(null, result);
    }).fail(function (defer, err) {
        // 处理error
        defer(err);
    });
}

getFileAsync().then(function (defer, file) {
    // ....
}).fail(function(defer, err) {
    // ....
});

Who Used

Examples

参见demo——test.js

更多使用案例请参考jsGen源代码!

npm loves you