From c3a920eea69762559ec39a3a84dca99faa542674 Mon Sep 17 00:00:00 2001 From: huangjue Date: Tue, 6 Oct 2015 23:42:15 +0800 Subject: [PATCH] 20151006 --- .gitignore | 1 + lib/async.js | 438 +++++++++++++++++++++++++++++++++++---------------- 2 files changed, 307 insertions(+), 132 deletions(-) diff --git a/.gitignore b/.gitignore index 9134974d9..8c0f40f26 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ perf/versions nyc_output coverage *.log +.idea diff --git a/lib/async.js b/lib/async.js index 198e0b5fb..1de133f53 100644 --- a/lib/async.js +++ b/lib/async.js @@ -8,36 +8,60 @@ (function () { var async = {}; + + /** + * 空函数 + */ function noop() {} function identity(v) { return v; } + + /** + * 返回布尔值快捷方法 + * @param v 判断布尔值的变量 + * @returns {boolean} + */ function toBool(v) { return !!v; } + + /** + * 取非的判断布尔值的方法 + * @param v 需要判断的变量 + * @returns {boolean} + */ function notId(v) { return !v; } // global on the server, window in the browser + var previous_async; // Establish the root object, `window` (`self`) in the browser, `global` // on the server, or `this` in some virtual machines. We use `self` // instead of `window` for `WebWorker` support. var root = typeof self === 'object' && self.self === self && self || - typeof global === 'object' && global.global === global && global || - this; + typeof global === 'object' && global.global === global && global || + this; + //保存已有的名为async的变量值 if (root != null) { previous_async = root.async; } + //async变量无冲突时赋值 async.noConflict = function () { root.async = previous_async; return async; }; + /** + * 只调用一次回调的方,重复调用会抛错 + * @param fn 只调用一次的函数 + * @returns {Function} + */ function only_once(fn) { return function() { if (fn === null) throw new Error("Callback was already called."); @@ -46,6 +70,12 @@ }; } + /** + * 不抛错的调用一次回调的方法 + * @param fn 只调用一次的函数 + * @returns {Function} + * @private + */ function _once(fn) { return function() { if (fn === null) return; @@ -58,31 +88,60 @@ var _toString = Object.prototype.toString; + //判断变量是否为数组,isArray方法为ECMAScript5.1中的方法 var _isArray = Array.isArray || function (obj) { - return _toString.call(obj) === '[object Array]'; - }; + return _toString.call(obj) === '[object Array]'; + }; // Ported from underscore.js isObject + /** + * 判断是否为对象,参考underscore.js? + * @param obj 需要判断的变量 + * @returns {boolean} + * @private + */ var _isObject = function(obj) { var type = typeof obj; return type === 'function' || type === 'object' && !!obj; }; + /** + * 判断是否为数组或者是带有length属性的对象 + * @param arr 需要判断的数组 + * @returns {boolean} + * @private + */ function _isArrayLike(arr) { return _isArray(arr) || ( - // has a positive integer length property - typeof arr.length === "number" && - arr.length >= 0 && - arr.length % 1 === 0 - ); + // has a positive integer length property + // 有一个正整数长度属性 + typeof arr.length === "number" && + //避免数值为NaN + arr.length >= 0 && + //避免数值为Infinity + arr.length % 1 === 0 + ); } + /** + * 遍历整个数组或者对象 + * @param coll 需要遍历的变量,Array或Object + * @param iterator 遍历函数 + * @returns {*} + * @private + */ function _each(coll, iterator) { return _isArrayLike(coll) ? _arrayEach(coll, iterator) : _forEachOf(coll, iterator); } + /** + * 遍历数组 + * @param arr 需要遍历的数组 + * @param iterator 遍历函数 + * @private + */ function _arrayEach(arr, iterator) { var index = -1, length = arr.length; @@ -92,9 +151,17 @@ } } + /** + * map方法遍历整个数组 + * @param arr 遍历数组 + * @param iterator 遍历函数 + * @returns {Array} + * @private + */ function _map(arr, iterator) { var index = -1, length = arr.length, + //当把构造函数作为函数调用,不使用 new 运算符时,它的行为与使用 new 运算符调用它时的行为完全一样 result = Array(length); while (++index < length) { @@ -103,10 +170,24 @@ return result; } + /** + * 返回从0开始到count-1的数组 + * @param count 从0开始范围截止长度 + * @returns {Array} + * @private + */ function _range(count) { return _map(Array(count), function (v, i) { return i; }); } + /** + * 返回对整个数组执行操作后的值 + * @param arr 需要遍历的数组 + * @param iterator 遍历函数 + * @param memo 初始值 + * @returns {*} + * @private + */ function _reduce(arr, iterator, memo) { _arrayEach(arr, function (x, i, a) { memo = iterator(memo, x, i, a); @@ -114,12 +195,25 @@ return memo; } + /** + * 对象遍历方法 + * @param object 需要遍历的对象 + * @param iterator 遍历函数 + * @private + */ function _forEachOf(object, iterator) { _arrayEach(_keys(object), function (key) { iterator(object[key], key); }); } + /** + * 数组查找方法 + * @param arr 被查询的数组 + * @param item 查找的变量 + * @returns {number} + * @private + */ function _indexOf(arr, item) { for (var i = 0; i < arr.length; i++) { if (arr[i] === item) return i; @@ -127,16 +221,28 @@ return -1; } + /** + * 获取对象的可遍历的key值 + * @param obj 被获取key的对象 + * @returns {Array} + * @private + */ var _keys = Object.keys || function (obj) { - var keys = []; - for (var k in obj) { - if (obj.hasOwnProperty(k)) { - keys.push(k); + var keys = []; + for (var k in obj) { + if (obj.hasOwnProperty(k)) { + keys.push(k); + } } - } - return keys; - }; + return keys; + }; + /** + * 调用一次获取一次数组或对象的下一个值的方法,超出长度返回null + * @param coll 被遍历的数组或者对象 + * @returns {Function} + * @private + */ function _keyIterator(coll) { var i = -1; var len; @@ -160,7 +266,15 @@ // Similar to ES6's rest param (http://ariya.ofilabs.com/2013/03/es6-and-rest-parameter.html) // This accumulates the arguments passed into an array, after a given index. // From underscore.js (https://github.com/jashkenas/underscore/pull/2140). + /** + * 将函数参数分段,参考rest param方式取出第一个参数,将其与后面的参数分开进行掉用 + * @param func 参数被分段的函数 + * @param startIndex 分段位置,值传入应为0或1 + * @returns {Function} + * @private + */ function _restParam(func, startIndex) { + //func.length是函数中参数长度,+号可以把string转为number startIndex = startIndex == null ? func.length - 1 : +startIndex; return function() { var length = Math.max(arguments.length - startIndex, 0); @@ -182,6 +296,12 @@ }; } + /** + * 去掉参数中的index参数 + * @param iterator 遍历函数 + * @returns {Function} + * @private + */ function _withoutIndex(iterator) { return function (value, index, callback) { return iterator(value, callback); @@ -195,6 +315,12 @@ // capture the global reference to guard against fakeTimer mocks var _setImmediate = typeof setImmediate === 'function' && setImmediate; + /** + * 延时执行,讲函数放到js队列尾部执行 + * @param fn 延时执行的参数 + * @type {Function} + * @private + */ var _delay = _setImmediate ? function(fn) { // not a direct alias for IE10 compatibility _setImmediate(fn); @@ -202,6 +328,7 @@ setTimeout(fn, 0); }; + //判断是否处于Node环境中,在Node环境中可以直接调用nextTick在下一个时间片种执行函数,否则使用添加到队列末尾的方法 if (typeof process === 'object' && typeof process.nextTick === 'function') { async.nextTick = process.nextTick; } else { @@ -209,88 +336,111 @@ } async.setImmediate = _setImmediate ? _delay : async.nextTick; - + /** + * 对集合中的每一个变量都执行同一个异步操作 + * @param arr 被遍历的集合 + * @param iterator 遍历函数,只有变量本身和回调函数两个参数 + * @param callback 回调函数,可选 + */ async.forEach = - async.each = function (arr, iterator, callback) { - return async.eachOf(arr, _withoutIndex(iterator), callback); - }; + async.each = function (arr, iterator, callback) { + return async.eachOf(arr, _withoutIndex(iterator), callback); + }; async.forEachSeries = - async.eachSeries = function (arr, iterator, callback) { - return async.eachOfSeries(arr, _withoutIndex(iterator), callback); - }; + async.eachSeries = function (arr, iterator, callback) { + return async.eachOfSeries(arr, _withoutIndex(iterator), callback); + }; async.forEachLimit = - async.eachLimit = function (arr, limit, iterator, callback) { - return _eachOfLimit(limit)(arr, _withoutIndex(iterator), callback); - }; + async.eachLimit = function (arr, limit, iterator, callback) { + return _eachOfLimit(limit)(arr, _withoutIndex(iterator), callback); + }; + /** + * 对集合中的每一个变量都执行同一个异步操作 + * @param arr 被遍历的集合 + * @param iterator 遍历函数,有变量本身,key和回调函数三个参数 + * @param callback 回调函数,可选 + */ async.forEachOf = - async.eachOf = function (object, iterator, callback) { - callback = _once(callback || noop); - object = object || []; - var size = _isArrayLike(object) ? object.length : _keys(object).length; - var completed = 0; - if (!size) { - return callback(null); - } - _each(object, function (value, key) { - iterator(object[key], key, only_once(done)); - }); - function done(err) { - if (err) { - callback(err); - } - else { - completed += 1; - if (completed >= size) { - callback(null); - } - } - } - }; - - async.forEachOfSeries = - async.eachOfSeries = function (obj, iterator, callback) { - callback = _once(callback || noop); - obj = obj || []; - var nextKey = _keyIterator(obj); - var key = nextKey(); - function iterate() { - var sync = true; - if (key === null) { + async.eachOf = function (object, iterator, callback) { + callback = _once(callback || noop); + object = object || []; + var size = _isArrayLike(object) ? object.length : _keys(object).length; + var completed = 0; + if (!size) { return callback(null); } - iterator(obj[key], key, only_once(function (err) { + _each(object, function (value, key) { + iterator(object[key], key, only_once(done)); + }); + function done(err) { if (err) { callback(err); } else { - key = nextKey(); - if (key === null) { - return callback(null); - } else { - if (sync) { - async.setImmediate(iterate); + completed += 1; + if (completed >= size) { + callback(null); + } + } + } + }; + + /** + * 按次序串行遍历集合中的每个元素执行遍历函数,最后执行callback + * @param obj 被遍历的集合 + * @param iterator 遍历函数 + * @param callback 回调函数 + */ + async.forEachOfSeries = + async.eachOfSeries = function (obj, iterator, callback) { + callback = _once(callback || noop); + obj = obj || []; + var nextKey = _keyIterator(obj); + var key = nextKey(); + function iterate() { + var sync = true; + if (key === null) { + return callback(null); + } + iterator(obj[key], key, only_once(function (err) { + if (err) { + callback(err); + } + else { + key = nextKey(); + if (key === null) { + return callback(null); } else { - iterate(); + if (sync) { + async.setImmediate(iterate); + } else { + iterate(); + } } } - } - })); - sync = false; - } - iterate(); - }; + })); + sync = false; + } + iterate(); + }; async.forEachOfLimit = - async.eachOfLimit = function (obj, limit, iterator, callback) { - _eachOfLimit(limit)(obj, iterator, callback); - }; + async.eachOfLimit = function (obj, limit, iterator, callback) { + _eachOfLimit(limit)(obj, iterator, callback); + }; + /** + * 遍历对象时同时存在几个函数运算并行 + * @param limit 并行数目 + * @returns {Function} + * @private + */ function _eachOfLimit(limit) { return function (obj, iterator, callback) { @@ -334,23 +484,47 @@ }; } - + /** + * 返回一个由函数参数调用遍历集合的方法 + * @param fn 构造函数 + * @returns {Function} + */ function doParallel(fn) { return function (obj, iterator, callback) { return fn(async.eachOf, obj, iterator, callback); }; } + + /** + * 返回一个由函数参数调用的N个并行遍历集合的方法 + * @param fn + * @returns {Function} + */ function doParallelLimit(fn) { return function (obj, limit, iterator, callback) { return fn(_eachOfLimit(limit), obj, iterator, callback); }; } + + /** + * 返回一个由函数参数调用的串行遍历集合的方法 + * @param fn 构造函数 + * @returns {Function} + */ function doSeries(fn) { return function (obj, iterator, callback) { return fn(async.eachOfSeries, obj, iterator, callback); }; } + /** + * 通过不同的遍历方式的函数来构造一个map方法遍历集合 + * @param eachfn 遍历方式 + * @param arr 遍历集合 + * @param iterator 遍历函数 + * @param callback 回调函数 + * @private + */ function _asyncMap(eachfn, arr, iterator, callback) { callback = _once(callback || noop); var results = []; @@ -371,23 +545,23 @@ // reduce only has a series version, as doing reduce in parallel won't // work in many situations. async.inject = - async.foldl = - async.reduce = function (arr, memo, iterator, callback) { - async.eachOfSeries(arr, function (x, i, callback) { - iterator(memo, x, function (err, v) { - memo = v; - callback(err); - }); - }, function (err) { - callback(err || null, memo); - }); - }; + async.foldl = + async.reduce = function (arr, memo, iterator, callback) { + async.eachOfSeries(arr, function (x, i, callback) { + iterator(memo, x, function (err, v) { + memo = v; + callback(err); + }); + }, function (err) { + callback(err || null, memo); + }); + }; async.foldr = - async.reduceRight = function (arr, memo, iterator, callback) { - var reversed = _map(arr, identity).reverse(); - async.reduce(reversed, memo, iterator, callback); - }; + async.reduceRight = function (arr, memo, iterator, callback) { + var reversed = _map(arr, identity).reverse(); + async.reduce(reversed, memo, iterator, callback); + }; function _filter(eachfn, arr, iterator, callback) { var results = []; @@ -408,13 +582,13 @@ } async.select = - async.filter = doParallel(_filter); + async.filter = doParallel(_filter); async.selectLimit = - async.filterLimit = doParallelLimit(_filter); + async.filterLimit = doParallelLimit(_filter); async.selectSeries = - async.filterSeries = doSeries(_filter); + async.filterSeries = doSeries(_filter); function _reject(eachfn, arr, iterator, callback) { _filter(eachfn, arr, function(value, cb) { @@ -453,12 +627,12 @@ } async.any = - async.some = _createTester(async.eachOf, toBool, identity); + async.some = _createTester(async.eachOf, toBool, identity); async.someLimit = _createTester(async.eachOfLimit, toBool, identity); async.all = - async.every = _createTester(async.eachOf, notId, notId); + async.every = _createTester(async.eachOf, notId, notId); async.everyLimit = _createTester(async.eachOfLimit, notId, notId); @@ -561,8 +735,8 @@ } function ready() { return _reduce(requires, function (a, x) { - return (a && results.hasOwnProperty(x)); - }, true) && !results.hasOwnProperty(k); + return (a && results.hasOwnProperty(x)); + }, true) && !results.hasOwnProperty(k); } if (ready()) { task[task.length - 1](taskCallback, results); @@ -1037,8 +1211,8 @@ async.log = _console_fn('log'); async.dir = _console_fn('dir'); /*async.info = _console_fn('info'); - async.warn = _console_fn('warn'); - async.error = _console_fn('error');*/ + async.warn = _console_fn('warn'); + async.error = _console_fn('error');*/ async.memoize = function (fn, hasher) { var memo = {}; @@ -1103,13 +1277,13 @@ } async.reduce(fns, args, function (newargs, fn, cb) { - fn.apply(that, newargs.concat([_restParam(function (err, nextargs) { - cb(err, nextargs); - })])); - }, - function (err, results) { - callback.apply(that, [err].concat(results)); - }); + fn.apply(that, newargs.concat([_restParam(function (err, nextargs) { + cb(err, nextargs); + })])); + }, + function (err, results) { + callback.apply(that, [err].concat(results)); + }); }); }; @@ -1124,9 +1298,9 @@ var that = this; var callback = args.pop(); return eachfn(fns, function (fn, _, cb) { - fn.apply(that, args.concat([cb])); - }, - callback); + fn.apply(that, args.concat([cb])); + }, + callback); }); if (args.length) { return go.apply(this, args); @@ -1182,27 +1356,27 @@ }); async.wrapSync = - async.asyncify = function asyncify(func) { - return _restParam(function (args) { - var callback = args.pop(); - var result; - try { - result = func.apply(this, args); - } catch (e) { - return callback(e); - } - // if result is Promise object - if (_isObject(result) && typeof result.then === "function") { - result.then(function(value) { - callback(null, value); - })["catch"](function(err) { - callback(err.message ? err : new Error(err)); - }); - } else { - callback(null, result); - } - }); - }; + async.asyncify = function asyncify(func) { + return _restParam(function (args) { + var callback = args.pop(); + var result; + try { + result = func.apply(this, args); + } catch (e) { + return callback(e); + } + // if result is Promise object + if (_isObject(result) && typeof result.then === "function") { + result.then(function(value) { + callback(null, value); + })["catch"](function(err) { + callback(err.message ? err : new Error(err)); + }); + } else { + callback(null, result); + } + }); + }; // Node.js if (typeof module === 'object' && module.exports) {