系列文章:

1、async-validator 源码学习(一):文档翻译

2、async-validator 源码学习笔记(二):目录结构

3、async-validator 源码学习笔记(三):rule

4、async-validator 源码学习笔记(四):validator

5、async-validator 源码学习笔记(五):Schema

一、validate 介绍

validate 是 async-validator 的核心方法,不仅需要掌握它的使用,也需要了解它的原理。

使用

validator.validate( source, [options], callback )
.then(()=>{})
.catch( ({errors, fields}) => {})

参数

  • source 是需要验证的对象
  • options 是描述验证的处理选项的对象
  • callback 校验完成的回调函数

返回值是一个 promise 对象

  • then 是校验通过执行。
  • catch 校验失败执行。
  • errors 是 error 的数组,fields 是一个对象,包含监听对象和 error 的数组。

validate 方法校验的流程为:

源码是如何定义 validate 方法的呢?

二、validate 源码解读

/*
参数:
source_ 即 source :校验的对象。
o 即 options :描述验证处理选项。
oc 即 callback:验证完成的回调函数。
*/
_proto.validate = function validate(source_, o, oc) {
...
var source = source_;
var options = o;
var callback = oc;
...
return asyncMap(series, options, function (data, doIt) {
....
},function (results) {
complete(results);
}, source);
};

validate 方法前半部分主要是在构造一个完整的 series 对象,返回的是 asyncMap 方法。我们来看看 validate 方法内部的几个方法,分别作用是什么。

参数处理

_proto.validate = function validate(source_, o, oc) {
...
var source = source_;
var options = o;
var callback = oc;
// options 是可选参数
// 如果 options 是函数时,说明第二个是回调函数,options 是空对象
if (typeof options === 'function') {
callback = options;
options = {};
}
...
};

检查校验规则

检查校验规则是否为空,为空的时候立即执行回调。

if (!this.rules || Object.keys(this.rules).length === 0) {
if (callback) {
callback(null, source);
}
return Promise.resolve(source);
}

complete 函数

complete 函数主要是为了整合 errors 数组和 fields 对象,然后用 callback 回调函数把它们返回。

_proto.validate = function validate(source_, o, oc) {
...
function complete(results) {
var errors = [];
var fields = {};
// 定义 add 方法,给 errors 添加元素 error
function add(e) {
if (Array.isArray(e)) {
var _errors;
// 给 errors 添加 error
errors = (_errors = errors).concat.apply(_errors, e);
} else {
errors.push(e);
}
}
// 迭代 resaults 把 resaults 中的每个 error 都加入 errors
for (var i = 0; i < results.length; i++) {
add(results[i]);
}
//如果最后结果 errors 为空,就返回 null
if (!errors.length) {
callback(null, source);
} else {
//把 errors 中相同 field 的 error 合并,转换为对象的形式
fields = convertFieldsError(errors);
// errors fields 回调传出参数
callback(errors, fields);
}
}
return asyncMap(series, options, function (data, doIt) {
....
},function (results) {
complete(results);
}, source);
};

options.message

messsage 主要是定义检验失败后的错误提示信息,官方提供了一个默认模板,我们也可以进行定制化,此处的 options.message 就是来处理到底使用哪个的?根据情况到底是使用默认还是合并。

_proto.validate = function validate(source_, o, oc) {
...
//如果 options 中有 message 属性
if (options.messages) {
// 创建一个 message ,使用的是默认
var messages$1 = this.messages();
if (messages$1 === messages) {
messages$1 = newMessages();
}
// 将options 的 message 与默认的 message 合并
deepMerge(messages$1, options.messages);
options.messages = messages$1;
} else {
// options 没有 message 属性
options.messages = this.messages();
}
return asyncMap(series, options, function (data, doIt) {
....
},function (results) {
complete(results);
}, source);
};

series 对象

生成的 serise 对象,目的是为了统一最终的数据格式。

_proto.validate = function validate(source_, o, oc) {
...
var series = {};
// keys 是 rule 的所有键
var keys = options.keys || Object.keys(this.rules);
keys.forEach(function (z) {
// arr 存放 rules[z] 的一个数组
var arr = _this2.rules[z];
// value 存放 source[z] 是一个值或对象
var value = source[z];
arr.forEach(function (r) {
var rule = r;
// 当有transform属性而且是个函数时,要提前把值转换
if (typeof rule.transform === 'function') {
// 浅拷贝下,打破引用
if (source === source_) {
source = _extends({}, source);
}
value = source[z] = rule.transform(value);
}
// 浅拷贝打破引用
if (typeof rule === 'function') {
rule = {
validator: rule
};
} else {
rule = _extends({}, rule);
} // Fill validator. Skip if nothing need to validate
rule.validator = _this2.getValidationMethod(rule);
// 异常处理
if (!rule.validator) {
return;
}
rule.field = z;
rule.fullField = rule.fullField || z;
rule.type = _this2.getType(rule);
// 生成完整的 series
series[z] = series[z] || [];
series[z].push({
rule: rule,
value: value,
source: source,
field: z
});
});
});
return asyncMap(series, options, function (data, doIt) {
....
},function (results) {
complete(results);
}, source);
};

asyncMap

asyncMap 作为一个返回函数,不得不说它又是什么内容呢?

异步迭代用的 asyncMap 函数并没有多长,它主要实现两个功能,第一是决定是串行还是并行的执行单步校验,第二个功能是实现异步,把整个迭代校验过程封装到一个 promise 中,实现了整体上的异步。

function asyncMap(objArr, option, func, callback, source) {
// 如果option.first选项为真,说明第一个error产生时就要报错
if (option.first) {
// pending 是一个promise
var _pending = new Promise(function (resolve, reject) {
// 定义一个函数next,这个函数先调用callback,参数是errors
// 再根据errors的长度决定resolve还是reject
var next = function next(errors) {
callback(errors);
// reject的时候,返回一个AsyncValidationError的实例
// 实例化时第一个参数是errors数组,第二个参数是对象类型的errors
return errors.length ? reject(new AsyncValidationError(errors, convertFieldsError(errors))) : resolve(source);
};
// 把对象扁平化为数组flattenArr
var flattenArr = flattenObjArr(objArr);
// 串行
asyncSerialArray(flattenArr, func, next);
});
// 捕获error
_pending["catch"](function (e) {
return e;
});
return _pending;
}
// 如果option.first选项为假,说明所有的error都产生时才报错
// 当指定字段的第一个校验规则产生error时调用callback,不再继续处理相同字段的校验规则。
var firstFields = option.firstFields === true ? Object.keys(objArr) : option.firstFields || [];
var objArrKeys = Object.keys(objArr);
var objArrLength = objArrKeys.length;
var total = 0;
var results = [];
// 这里定义的函数next和上面的类似,只不过多了total的判断
var pending = new Promise(function (resolve, reject) {
var next = function next(errors) {
results.push.apply(results, errors);
// 只有全部的校验完才能执行最后的callback和reject
total++; if (total === objArrLength) {
// 这个callback和reject/resolve是这个库既能回调函数又能promise的核心
callback(results);
return results.length ? reject(new AsyncValidationError(results, convertFieldsError(results))) : resolve(source);
}
}; if (!objArrKeys.length) {
callback(results);
resolve(source);
}
// 当firstFields中指定了该key时,说明该字段的第一个校验失败产生时就停止并调用callback
// 所以是串行的asyncSerialArray
// 没有指定该key,说明该字段的校验error需要都产生,就并行asyncParallelArray
objArrKeys.forEach(function (key) {
var arr = objArr[key]; if (firstFields.indexOf(key) !== -1) {
asyncSerialArray(arr, func, next);
} else {
asyncParallelArray(arr, func, next);
}
});
});
// 捕获error,添加错误处理
pending["catch"](function (e) {
return e;
});
// 返回promise实例
return pending;
}

async-validator 源码学习笔记(六):validate 方法的更多相关文章

  1. yii2源码学习笔记(六)

    Behvaior类,Behavior类是所有事件类的基类: 目录yii2\base\Behavior.php <?php /** * @link http://www.yiiframework. ...

  2. async-validator 源码学习笔记(四):validator

    系列文章: 1.async-validator 源码学习(一):文档翻译 2.async-validator 源码学习笔记(二):目录结构 3.async-validator 源码学习笔记(三):ru ...

  3. async-validator 源码学习笔记(五):Schema

    系列文章: 1.async-validator 源码学习(一):文档翻译 2.async-validator 源码学习笔记(二):目录结构 3.async-validator 源码学习笔记(三):ru ...

  4. RocketMQ 源码学习笔记————Producer 是怎么将消息发送至 Broker 的?

    目录 RocketMQ 源码学习笔记----Producer 是怎么将消息发送至 Broker 的? 前言 项目结构 rocketmq-client 模块 DefaultMQProducerTest ...

  5. RocketMQ 源码学习笔记 Producer 是怎么将消息发送至 Broker 的?

    目录 RocketMQ 源码学习笔记 Producer 是怎么将消息发送至 Broker 的? 前言 项目结构 rocketmq-client 模块 DefaultMQProducerTest Roc ...

  6. async-validator 源码学习笔记(三):rule

    系列文章: 1.async-validator 源码学习(一):文档翻译 2.async-validator 源码学习笔记(二):目录结构 rule 主要实现的是校验规则,文件结构为下图: 一.rul ...

  7. JUC源码学习笔记2——AQS共享和Semaphore,CountDownLatch

    本文主要讲述AQS的共享模式,共享和独占具有类似的套路,所以如果你不清楚AQS的独占的话,可以看我的<JUC源码学习笔记1> 主要参考内容有<Java并发编程的艺术>,< ...

  8. JUC源码学习笔记4——原子类,CAS,Volatile内存屏障,缓存伪共享与UnSafe相关方法

    JUC源码学习笔记4--原子类,CAS,Volatile内存屏障,缓存伪共享与UnSafe相关方法 volatile的原理和内存屏障参考<Java并发编程的艺术> 原子类源码基于JDK8 ...

  9. JUC源码学习笔记5——线程池,FutureTask,Executor框架源码解析

    JUC源码学习笔记5--线程池,FutureTask,Executor框架源码解析 源码基于JDK8 参考了美团技术博客 https://tech.meituan.com/2020/04/02/jav ...

随机推荐

  1. suse 12 二进制部署 Kubernetets 1.19.7 - 第03章 - 部署flannel插件

    文章目录 1.3.部署flannel网络 1.3.0.下载flannel二进制文件 1.3.1.创建flannel证书和私钥 1.3.2.生成flannel证书和私钥 1.3.3.将pod网段写入et ...

  2. suse 12 脚本部署docker(二进制文件)

    suse-linux:~ # cat /etc/issue Welcome to SUSE Linux Enterprise Server 12 SP3 (x86_64) - Kernel \r (\ ...

  3. 显式锁之ReentrantLock实现

    下图是Lock接口清单,定义了一些抽象的锁操作.Java本身提供了内部锁机制,那么还需要显示Lock,何用?与内部加锁机制不同,Lock提供了无条件.可轮询.定时.可中断的锁获取操作:所有加锁和解锁的 ...

  4. CPU平均负载率之stress模拟CPU密集型进程

    一.对CPU密集型进程进行模拟,具体如下: 第一个终端 在第一个终端运行 stress 命令,模拟一个 CPU 使用率 100% 的场景:stress --cpu 1 -- timeout 600 第 ...

  5. ASP.NET Core 6框架揭秘实例演示[08]:配置的基本编程模式

    .NET的配置支持多样化的数据源,我们可以采用内存的变量.环境变量.命令行参数.以及各种格式的配置文件作为配置的数据来源.在对配置系统进行系统介绍之前,我们通过几个简单的实例演示一下如何将具有不同来源 ...

  6. PostgreSQL VACUUM 之深入浅出 (二)

    AUTOVACUUM AUTOVACUUM 简介 PostgreSQL 提供了 AUTOVACUUM 的机制. autovacuum 不仅会自动进行 VACUUM,也会自动进行 ANALYZE,以分析 ...

  7. 『无为则无心』Python面向对象 — 56、Python多态

    目录 1.Python中的多态 (1)多态的定义 (2)多态的好处 (3)多态实现步骤 2.体验多态 1.Python中的多态 (1)多态的定义 多态是一种使用对象的方式,子类重写父类方法,不同的子类 ...

  8. spyeye手机监控最新版分享下载不用碰对方手机,所有聊天内容,照片等都能看见

    spyeye间谍眼介绍的非常详细,不但可以监控用户收发短信和通话记录,还可远程开启手机听筒,监听手机周围声音,实时监控用户的通话,并且利用GPS功能监测到手机用户所在位置,给用户安全隐私造成极大的威胁 ...

  9. 【缓存】CPU高速缓存 之MESI 性协议 Gif 动画

    CPU缓存架构 不同的CPU厂商的架构也有些不同,在这里只介绍流行的缓存架构 缓存一致性可以分为三个点: 在进程每个写入运算时都立刻采取措施保证资料一致性 每个独立的运算,假如它造成资料值的改变,所有 ...

  10. 【C# 程序集】在.net中使用GAC 全局程序集缓存

    原文地址:https://blog.alswl.com/2011/01/gac/ GAC GAC是什么?是用来干嘛的?GAC的全称叫做全局程序集缓存,通俗的理解就是存放各种.net平台下面需要使用的d ...