async源码学习 - 控制流程waterfall函数
waterfall函数会连续执行数组中的函数,每次通过数组下一个函数的结果。
然而,数组任务中的任意一个函数结果传递失败,那么该函数的下一个函数将不会执行,
并且主回调函数立马把错误作为参数执行。 以上是我翻译的,我都不知道翻译的什么鬼。 其实该函数的作用就是: 上一个异步函数返回结果可以传给下一个异步函数,如果传递过程中,第一个参数出错了也就是真值的话,
下一个回调函数将会停止调用,并且直接调用waterfall函数的第二个参数,其实也是个回调函数。并且把错误的参数传过去。 先看看官网的demo:
async.waterfall([
fn1,
fn2,
fn3
], function(err, arg1) {
console.log(err);
console.log(arg1);
}); function fn1(next) {
next(null, '11');
}; function fn2(arg1, next) {
console.log(arg1);
next(null, '22', '33');
}; function fn3(arg1, arg2, next) {
console.log(arg1);
console.log(arg2);
next(null, 'done');
};
async.waterfall([function(aaa) {
console.log(11);
aaa(null, 'one');
}, function(arg1, bbb) {
console.log(arg1);
bbb(null, 'two', 'three');
}, function(arg1, arg2, ccc) {
console.log(arg1);
console.log(arg2);
ccc(null, 'down', 'down2');
}], function(err, result, res2) {
console.log(err);
console.log(result);
console.log(res2);
});
自己搞个创建文件的实例,看看
class File {
constructor() {}
// 创建文件
createFile(callback) {
setTimeout(() => {
if (0) {
console.log('创建文件失败');
callback('err');
} else {
console.log('创建文件成功');
callback(null);
};
}, 3000);
}
// 写文件
writeFile(callback) {
setTimeout(() => {
if (1) {
console.log('写文件失败');
callback('err');
} else {
console.log('写文件成功');
callback(null);
};
}, 2000);
}
// 读文件
readFile(callback) {
setTimeout(() => {
if (0) {
console.log('读文件失败');
callback('err');
} else {
console.log('读文件成功');
callback(null, 'I love async!');
};
}, 4000);
}
};
let file = new File();
async.waterfall([function(callback) {
file.createFile(function(err) {
if (!err) {
callback(null, 'createFile Ok');
} else {
callback('createFileFail');
};
});
}, function(err, callback) {
file.writeFile(function(err) {
if (!err) {
callback(null, 'writeFile Ok');
} else {
callback('writeFileFail');
};
});
}, function(err, callback) {
file.readFile(function(err) {
if (!err) {
callback(null, 'readFile Ok');
} else {
callback('readFileFail');
};
});
}], function(err, result) {
console.log(err);
console.log(result);
});
我一直纳闷,他怎么做到,上一个异步什么时候做完后,通知下一个异步开始执行,并且把参数传给下一个异步函数的。看看源码实现:
/**
* Created by Sorrow.X on 2017/5/28.
*/ var waterfall = (function() { var isArray = Array.isArray; // 把数组的isArray赋给isArray变量 // 是否支持Symbol
var supportsSymbol = typeof Symbol === 'function'; var setImmediate$1 = wrap(_defer); function wrap(defer) {
return function (fn/*, ...args*/) {
var args = slice(arguments, 1);
defer(function () {
fn.apply(null, args);
});
};
}; // 是否是异步
function isAsync(fn) {
return supportsSymbol && fn[Symbol.toStringTag] === 'AsyncFunction';
}; // 空函数
function noop() {
// No operation performed.
}; // 一次(偏函数)
function once(fn) { // fn: waterfall的第二个参数(回调函数)
return function () {
if (fn === null) return;
var callFn = fn;
fn = null; // 把上级函数作用域中的fn置空
callFn.apply(this, arguments); // 调用回调函数
};
}; // 包装成异步
function wrapAsync(asyncFn) {
return isAsync(asyncFn) ? asyncify(asyncFn) : asyncFn;
}; function asyncify(func) {
return initialParams(function (args, callback) {
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) {
invokeCallback(callback, null, value);
}, function(err) {
invokeCallback(callback, err.message ? err : new Error(err));
});
} else {
callback(null, result);
}
});
}; function isObject(value) {
var type = typeof value;
return value != null && (type == 'object' || type == 'function');
}; function invokeCallback(callback, error, value) {
try {
callback(error, value);
} catch (e) {
setImmediate$1(rethrow, e);
}
}; // 重写数组中的slice方法
function slice(arrayLike, start) { // arrayLike: 类数组对象 start: 开始位置
start = start|0;
var newLen = Math.max(arrayLike.length - start, 0); // 长度
var newArr = Array(newLen); // 创建一个长度为newLen的数组
for(var idx = 0; idx < newLen; idx++) {
newArr[idx] = arrayLike[start + idx];
};
return newArr; // 返回数组
}; // 执行一次
function onlyOnce(fn) {
return function() {
if (fn === null) throw new Error("Callback was already called."); // 回调已被调用
var callFn = fn;
fn = null;
callFn.apply(this, arguments); //调用callFn 参数就是用户回调函数中的参数
};
}; var waterfall = function(tasks, callback) { // tasks: 异步函数数组容器, callback: 回调
callback = once(callback || noop); // 回调函数
if (!isArray(tasks)) return callback(new Error('First argument to waterfall must be an array of functions')); // 第一个参数必须是数组(函数数组)!
if (!tasks.length) return callback(); // 空数组的话直接调用回调函数(无参数)
var taskIndex = 0; // 任务索引 function nextTask(args) { // 参数数组
var task = wrapAsync(tasks[taskIndex++]); // 数组中的任务
args.push(onlyOnce(next)); // 把next方法添加到args数组中去
task.apply(null, args); // 调用数组中task函数(参数是数组)
}; function next(err/*, ...args*/) { // 其实就是函数参数中的回调函数callback
if (err || taskIndex === tasks.length) { // 只要有错误或者函数数组任务都完成了
return callback.apply(null, arguments); // 就执行回调
};
nextTask(slice(arguments, 1)); // 数组中的函数没循环完且没出错,那就继续调用
}; nextTask([]); // 调用
};
}());
waterfall函数中有个next方法,其实我们写的回调就是next方法。 好吧,以上代码直接抽取async中的代码,可以直接使用。如果只想要这一个功能的话。
async源码学习 - 控制流程waterfall函数的更多相关文章
- Netty 源码学习——客户端流程分析
Netty 源码学习--客户端流程分析 友情提醒: 需要观看者具备一些 NIO 的知识,否则看起来有的地方可能会不明白. 使用版本依赖 <dependency> <groupId&g ...
- async源码学习 - 全部源码
因为工作需要,可能我离前端走远了,偏node方向了.所以异步编程的需求很多,于是乎,不得不带着学习async了. 我有个习惯,用别人的东西之前,喜欢稍微搞明白点,so就带着看看其源码. github: ...
- live555源码学习1---Socket流程架构图
怎么说呢,换了工作环境,好多软件公司禁止使用了,有道笔记也无法使用了.发现博客园还可以上传图片,以后只能在这里记录了. 越发的感觉需要尽快把live555的代码拿下.因为工作环境问题,webrtc的源 ...
- ss源码学习--工作流程
ss的local端和server端的工作流程相似,因此复用了TCPRelay类和TCPRelayHandler类. 两端均是使用TCPRelay类监听连接,并使用TCPRelayHandler类处理请 ...
- .17-浅析webpack源码之compile流程-入口函数run
本节流程如图: 现在正式进入打包流程,起步方法为run: Compiler.prototype.run = (callback) => { const startTime = Date.now( ...
- [Go语言]从Docker源码学习Go——结构和函数的定义
Docker在最近很火,而作为Docker的开发语言-Go也再次被大家提到. 已经使用Docker一段时间了,但是对于源码,尤其是其开发语言Go却一直是一知半解. 最近准备利用空余时间从Docker源 ...
- [Android FrameWork 6.0源码学习] ViewGroup的addView函数分析
Android中整个的View的组装是采用组合模式. ViewGroup就相当与树根,各种Layout就相当于枝干,各种子View,就相当于树叶. 至于View类.我们就当它是个种子吧.哈哈! Vie ...
- Netty 源码学习——EventLoop
Netty 源码学习--EventLoop 在前面 Netty 源码学习--客户端流程分析中我们已经知道了一个 EventLoop 大概的流程,这一章我们来详细的看一看. NioEventLoopGr ...
- Hadoop源码学习笔记(1) ——第二季开始——找到Main函数及读一读Configure类
Hadoop源码学习笔记(1) ——找到Main函数及读一读Configure类 前面在第一季中,我们简单地研究了下Hadoop是什么,怎么用.在这开源的大牛作品的诱惑下,接下来我们要研究一下它是如何 ...
随机推荐
- 使用 json-server 模拟数据
1. 先安装 npm install json-server -g 2.查看是否安装成功 json-server -h 3.准备数据,新建一个文件夹 mock,cd mock,在mock下 新建tes ...
- js 中导出excel 较长数字串会变成科学计数法
在做项目中,碰到如题的问题.比如要将居民的信息导出到excel中,居民的身份证号码因为长度过长(大于10位),excel会自动的将过长的数字串转换成 科学计数法.现在网上找到解决方案之一: (在数字串 ...
- element-ui中table表头表格错误问题解决
我用的是element-ui v1.4.3 在iframe关闭和切换导航会引起有table的表格错位,解决办法: handleAdminNavTab: function(tab) { var admi ...
- 2017-10-10 都市传说: "部分"中文出现乱码
知乎原链, 作者亦本人 事情起源于项目另一开发者在中文Windows下构建时遇到的部分中文出现乱码问题. 当时很不解的是, 为什么会只有部分出现乱码. 第一感觉是, 如果编码转换不正确, 要么全乱码, ...
- 遇到了ImportError: libmysqlclient_r.so.16: cannot open shared object file: No such file or directory
解决方法如下: 1. 通过命令查找libmysqlclient_r.so.16 在什么地方,一般是在/usr/lib64/mysql/下面 2. 做一个链接到/usr/lib64 下: ln -s / ...
- 你不可不知的Java引用类型之——软引用
定义 软引用是使用SoftReference创建的引用,强度弱于强引用,被其引用的对象在内存不足的时候会被回收,不会产生内存溢出. 说明 软引用,顾名思义就是比较"软"一点的引用. ...
- Python之逻辑回归
代码: import numpy as np from sklearn import datasets from sklearn.linear_model import LogisticRegress ...
- 高通移植mipi LCD的过程LK代码
lk部分:(实现LCD兼容) 1. 函数定位 aboot_init()来到target_display_init(): 这就是高通原生lk LCD 兼容的关键所在.至于你需要兼容多少LCD 就在whi ...
- [cb]Unity 关卡编辑器 开发
关卡编辑器 关卡编辑器插件开发日记 1. Apply to Prefab [替换Prefab] if (GUILayout.Button("Apply Collider To Prefab& ...
- 戴尔dell服务器硬件故障SMTP邮件报警idrac配置
上次公司有台戴尔的服务器硬盘故障了却没有及时发现,后面就研究了一下看到戴尔的idrac有硬件SMTP报警功能 然后自己折腾了一下,一开始配置不成功,后面问了一下戴尔官方的最好自己再摸索了一下解决了,做 ...