rwson

rwson

一个前端开发

编写你自己的async.waterfall

在早期的异步开发中,如果有一些异步任务需要处理,难免会遇到回调地狱,为了解决这种问题,也出现过很多第三方库来避免,其中async.js就是比较有名的一个,里面有个waterfall方法,本文我们一起来模拟实现一个类似的

先来看下调用

waterfall([
	function(cb) {
		console.log(new Date);
		setTimeout(function() {
			cb(null, 123);
		}, 2000);
	},
	function(arg, cb) {
		console.log(new Date);
		setTimeout(function() {
			console.log(arg);
			cb(null, 123, 456);
		}, 2000);
	},
	function(arg1, arg2, cb) {
		console.log(new Date);
		console.log(arg1, arg2);
	}
], function(ex) {
	if (ex) {
		throw ex;
	}
});

下面我们一起来看下实现下waterfall这个方法 :

/**
 * @param task 任务队列
 * @param callback 最后的回调
 **/
module.exports = function(task = [], callback = noop) {
  	//	类型判断
    if (!(task instanceof Array)) {
        return callback(new Error("task should be an array!"));
    }
  
  
    (function next(...args) {
        //  第一个参数如果不为空就直接执行callback
        if (args[0]) {
            return callback(args[0]);
        }
        if (task.length) {
            //  取得当前要执行的函数
            let fn = task.shift();
          	//	第一个参数是error相关的,所以从第二个开始截取
            fn.apply(null, [...args.slice(1), onlyOnce(next)]);
        } else {
            callback.apply(null, args);
        }
    })();
};

/**
 * 包装一个函数确保它只被执行一次
 **/
function onlyOnce(cb) {
    let flag = false;
    return function(...args) {
        if (flag) {
            return cb(new Error('cb already called'));
        }
        cb.apply(null, args);
        flag = true;
    };
}

function noop() {}

上面就是对waterfall方法的实现,在async.js还有很多其他很有用的方法,后面有机会继续模拟实现。