jQuery使用():Callbacks回调函数列表之异步编程(含源码分析)
- Callbacks的基本功能回调函数缓存与调用
- 特定需求模式的Callbacks
- Callbacks的模拟源码
一、Callbacks的基本功能回调函数缓存与调用
Callbacks即回调函数集合,在jQeury中配合异步操作实现异步回调任务,同时也具备函数解耦的基础特性。简单的说就是缓存回调函数的集合,所以这个方法应该具备的两个基础功能就是向集合中添加回调函数和触发回调函数的方法。先来看一下jQuery的这两个方法的使用:
- Callbacks.add() —— 添加回调函数
- Callbacks.fire() —— 执行回调函数
- function fon1(){
- console.log("函数一");
- }
- function fon2(a){
- console.log("函数二");
- }
- var call = $.Callbacks();//创建回调函数对象
- call.add(fon1,fon2);//添加回调函数
- call.fire();//执行回调函数
基于这样的基本功能模仿实现Callbacks的功能:
- function Callback(){
- var list = [];
- var self = {
- add:function(){
- if(arguments.length > 0){
- for(var ele in arguments){
- list.push(arguments[ele]);
- }
- }
- return this;
- },
- fire:function(){
- if(list.length > 0){
- for(var ele in list){
- list[ele]();
- }
- }
- return this;
- }
- }
- return self;
- }
上面的简单模仿可以实现添加回调函数和执行回调函数,接着来看jQuery实现的不同模式的回调列表对象。
二、特定需求模式的回调函数
1. 如果Callback只是上面模仿的那样的话,那就等于是回调函数裸奔,如果有需求是回调函数只能被触发一次,也就是说重复调用fire无效,要实现这个功能只需要在回调函数执行完以后将回调函数列的值修改成undefined即可,然后再在添加和执行函数上加一个判断阻断执行就可以。下面是修改过后的代码:
- //Callback源码
- function Callback(options){
- //list为undefined的时候(函数列表被禁用),主动触发disable禁用
- //once模式下回调函数列表执行一轮后禁用回调函数列表
- var list = [];
- options = options || "";
- var fire = function(data){
- //实现回调函数执行的方法
- if(list.length > 0){
- for(var ele in list){
- list[ele].apply(data[0],data[1]);
- }
- }
- if(options.indexOf("once") != -1){
- //清空函数列表,实现once只能调用fire执行一次的功能
- list = undefined;
- }
- }
- var self = {
- add:function(){
- if(list){//函数被锁定或者禁用就不能再添加回调函数了
- if(arguments.length > 0){
- for(var ele in arguments){
- list.push(arguments[ele]);
- }
- }
- }
- return this;
- },
- disable:function(){//禁用回调函数
- list = undefined;
- return this;
- },
- disabled:function(){//查看回调函数是否被禁用
- return !list;
- },
- fireWith:function(context,args){
- //因为fire作为作用域上的方法this指向window或者undefined
- //这里将this指向继续指向self,然后将类数组的args转换成数组(fireWith的功能)
- if(list){
- args = args || [];
- var data = [context,args.slice ? args.slcie() : args];
- fire(data);
- }
- return this;
- },
- fire:function(){
- //fire作为回调函数执行的API
- self.fireWith(this,arguments);
- return this;
- }
- }
- return self;
- }
实现“once”模式的回调函数列表之际代码只在14行~17行,然后add和fireWith方法内加一层判断即可。在实现“once”模式的时候考虑到后面还有其他模式和一些回调函数对象工具方法的实现,将fire()方法模仿jQuery源码提取到了对象作用域作为普通方法,回调函数对象上保留操作接口只负责调用执行,fireWith()用来调整执行的上下文和参数类型。顺便还实现了一个禁用回调函数方法disble方法。
2. 接着来看看第二个回调函数模式“memory”以及“once memory”的混合模式回调函数对象的实现,“memory”模式就是在触发一次函数列表以后,后面添加的函数会被自动触发,这个实现非常的简单,只要记录一下上一次执行的列表的最后一个回调函数的下标,不过要注意的是,如果是"once memory"是混合模式需要缓存参数,还有下标要置为0。具体代码如下,模仿的代码相对于jQuery源码中的Callbacks工具方法表面逻辑清晰一些,但是没有像jQuery源码那样极致的避免冗余,所以如果是学习源码的最优设计方案而非Callbacks的设计结构请绕路。
- //Callback源码
- function Callback(options){
- //list为undefined的时候(函数列表被禁用),主动触发disable禁用
- //once模式下回调函数列表执行一轮后禁用回调函数列表
- var list = [];
- options = options || "";
- var fired = false;
- var fireStart;
- var fireIndex;
- var memoryData = null;
- var fire = function(data){
- fireIndex = fireStart || 0;
- fireStart = 0;
- //实现回调函数执行的方法
- if(list.length > 0){
- for(;fireIndex < list.length; fireIndex++){
- list[fireIndex].apply(data[0],data[1]);
- }
- }
- if(options.indexOf("once") != -1){
- //清空函数列表,实现once只能调用fire执行一次的功能
- list = undefined;
- fireIndex = 0;
- }
- if(options.indexOf("memory") != -1){
- fireStart = fireIndex;
- fired = true;
- list = list || [];
- memoryData = data;
- }
- }
- var self = {
- add:function(){
- if(list){//函数被锁定或者禁用就不能再添加回调函数了
- if(arguments.length > 0){
- for(var ele in arguments){
- list.push(arguments[ele]);
- }
- }
- }
- if(options.indexOf("memory") != -1 && fired){
- fire(memoryData);
- }
- return this;
- },
- disable:function(){//禁用回调函数
- list = undefined;
- return this;
- },
- disabled:function(){//查看回调函数是否被禁用
- return !list;
- },
- fireWith:function(context,args){
- //直接调用API时表示fire()非add触发,所以fireStart要设置为0
- fireStart = 0;
- //因为fire作为作用域上的方法this指向window或者undefined
- //这里将this指向继续指向self,然后将类数组的args转换成数组(fireWith的功能)
- if(list){
- args = args || [];
- var data = [context,args.slice ? args.slcie() : args];
- fire(data);
- }
- return this;
- },
- fire:function(){
- //fire作为回调函数执行的API
- self.fireWith(this,arguments);
- return this;
- }
- }
- return self;
- }
三、Callbacks源码模仿实现
在第二部分主要是是实现了once、memory模式,再在这个基础上实现unique、stopOnFalse模式,本来项将remove、lock等这些回调对象的工具方法都实现,我还是放弃了,因为太简单了,实现起来又会占用很多页面,不方便理解更核心的代码,所以下面是在第二部分的基础上添加了unique、stopOnFalse模式的全部代码:
- //Callback源码
- function Callback(options){
- //list为undefined的时候(函数列表被禁用),主动触发disable禁用
- //once模式下回调函数列表执行一轮后禁用回调函数列表
- var list = [];
- options = options || "";
- var fired = false;
- var fireStart;
- var fireIndex;
- var memoryData = null;
- var memory = true;
- //stopOnFalse 模式表示中断执行,即使"memory stopOnFalse"混合模式,后面添加方法也不会触发执行,而必须callback.fire()方法来触发。
- var stopOnFalse = options.indexOf("stopOnFalse") != -1 ? true : false;
- var fire = function(data){
- fireIndex = fireStart || 0;
- fireStart = 0;
- //实现回调函数执行的方法
- if(list.length > 0){
- for(;fireIndex < list.length; fireIndex++){
- if( list[fireIndex].apply(data[0],data[1]) === false && stopOnFalse ){
- memory = false;
- break;
- }
- }
- }
- if(options.indexOf("once") != -1){
- //清空函数列表,实现once只能调用fire执行一次的功能
- list = undefined;
- fireIndex = 0;
- }
- if(options.indexOf("memory") != -1 && memory){
- fireStart = fireIndex;
- fired = true;
- list = list || [];
- memoryData = data;
- }
- }
- var self = {
- add:function(){
- if(list){//函数被锁定或者禁用就不能再添加回调函数了
- if(arguments.length > 0){
- if(options.indexOf("unique") != -1){//unique模式--不重复添加同一个函数
- for(var ele in arguments){
- if(list.indexOf(arguments[ele]) == -1){
- list.push(arguments[ele]);
- }
- }
- }else{
- for(var ele in arguments){
- list.push(arguments[ele]);
- }
- }
- }
- }
- if(options.indexOf("memory") != -1 && fired){
- fire(memoryData);
- }
- return this;
- },
- disable:function(){//禁用回调函数
- list = undefined;
- return this;
- },
- disabled:function(){//查看回调函数是否被禁用
- return !list;
- },
- fireWith:function(context,args){
- //直接调用API时表示fire()非add触发,所以fireStart要设置为0
- fireStart = 0;
- //因为fire作为作用域上的方法this指向window或者undefined
- //这里将this指向继续指向self,然后将类数组的args转换成数组(fireWith的功能)
- if(list){
- args = args || [];
- var data = [context,args.slice ? args.slice() : args];
- fire(data);
- }
- return this;
- },
- fire:function(){
- //fire作为回调函数执行的API
- self.fireWith(this,arguments);
- return this;
- }
- }
- return self;
- }
最后,这篇博客主要就是为了实现了Callbacks的源码,并非对其应用进行解析,毕竟在很多文档中都有很详细的说明,如果觉得文档不是很容易明白,可以参考这篇博客:jQuery.Callbacks源码及其应用分析
jQuery使用():Callbacks回调函数列表之异步编程(含源码分析)的更多相关文章
- Java异步编程——深入源码分析FutureTask
Java的异步编程是一项非常常用的多线程技术. 之前通过源码详细分析了ThreadPoolExecutor<你真的懂ThreadPoolExecutor线程池技术吗?看了源码你会有全新的认识&g ...
- Android异步消息传递机制源码分析
1.Android异步消息传递机制有以下两个方式:(异步消息传递来解决线程通信问题) handler 和 AsyncTask 2.handler官方解释的用途: 1).定时任务:通过handler.p ...
- jQuery 的 ready 函数是如何工作的?(源码分析)
如果你使用过 jQuery , 就必然使用过 ready 函数,它用来注册当页面准备好之后可以执行的函数. 问题来啦,我们的页面什么时候准备好了呢? 1. onload 事件 最基本的处理方式就是页面 ...
- iOS的异步绘制--YYAsyncLayer源码分析
iOS的异步渲染 最近看了YYAsyncLayer在这里总结一下.YYAsyncLayer是整个YYKit异步渲染的基础.整个项目的Github地址在这里.你可以先下载了一睹为快,也可以跟着我一步一步 ...
- jQuery使用():Deferred有状态的回调列表(含源码)
deferred的功能及其使用 deferred的实现原理及模拟源码 一.deferred的功能及其使用 deferred的底层是基于callbacks实现的,建议再熟悉callbacks的内部机制前 ...
- jQuery源码分析(九) 异步队列模块 Deferred 详解
deferred对象就是jQuery的回调函数解决方案,它解决了如何处理耗时操作的问题,比如一些Ajax操作,动画操作等.(P.s:紧跟上一节:https://www.cnblogs.com/grea ...
- jQuery 2.0.3 源码分析 Deferred概念
JavaScript编程几乎总是伴随着异步操作,传统的异步操作会在操作完成之后,使用回调函数传回结果,而回调函数中则包含了后续的工作.这也是造成异步编程困难的主要原因:我们一直习惯于“线性”地编写代码 ...
- jQuery 2.0.3 源码分析 Deferrred概念
转载http://www.cnblogs.com/aaronjs/p/3348569.html JavaScript编程几乎总是伴随着异步操作,传统的异步操作会在操作完成之后,使用回调函数传回结果,而 ...
- LiteOS-任务篇-源码分析-任务调度函数
目录 前言 笔录草稿 核心源码分析 osTaskSchedule函数源码分析 osPendSV函数源码分析 TaskSwitch函数源码分析 调度上层源码分析 osSchedule函数源码分析 LOS ...
随机推荐
- (办公)springmvc->controller的统一异常层,返回json
controller里面写的代码,很多时候,没有写try{}catch(Exceiption ex){},结果就是系统出错,就算是接口,参数正确也会返回404,这个是不应该的. 下面是代码,以后参考 ...
- Django项目结构介绍
官网下载网址:https://www.djangoproject.com/download/ 安装(安装最新LTS版): pip3 install django==2.0.7 创建一个django项目 ...
- ORA-02266错误的批量生成脚本解决方案
ORA-02266: unique/primary keys in table referenced by enabled foreign keys这篇博客是很早之前总结的一篇文章,最近导数时使用TR ...
- android测试用例编写
说明:android中写测试用例也是用junit,测试用例代码风格是junit3的风格.java中测试用例中使用junit3需要继承TestCase(junit4则不需要,直接用annotation即 ...
- 2018-2019-2 20175217 实验三《敏捷开发与XP实践》实验报告
一.实验报告封面 课程:Java程序设计 班级:1752班 姓名:吴一凡 学号:20175217 指导教师:娄嘉鹏 实验日期:2019年4月25日 实验时间:--- 实验序号:实验三 实验名称:敏捷开 ...
- html5 vedio 播放器,禁掉进度条快进快退事件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- Linux:Day17(下) openssl
Linux Services and Security OpenSSL OpenSSH dns:bind web:http,httpd(apache),php,mariadb(mysql) lamp ...
- django.db.utils.ProgrammingError: (1146, "Table 'db_gold.user_ip_info' doesn't exist") RuntimeError: Model class scanhosts.models.HostLoginInfo doesn't declare an explicit app_label and isn't in an a
Error Msg 创建了一个apps的目录将所有app放入apps文件中, 将apps路径加入sys.path中:sys.insert(0, os.path.join(BASE_DIR, " ...
- C#需要在项目程序生成前后执行相关的事件
分享4: 需求:需要在项目程序生成前后执行相关的事件,比如:需要将某个文件拷贝到bin\Debug中,或者创建某文件夹等. 分析:我们可利用项目属性(选择项目右键,选择属性)中的“生成事件”预定义相关 ...
- 你懂redis吗
一.redis简介 Redis是当前比较热门的NOSQL系统之一,它是一个开源的使用ANSI c语言编写的key-value存储系统(区别于MySQL的二维表格的形式存储.).和Memcache类似, ...