Zepto源码分析-callbacks模块

本文深入探讨了Zepto.js中的Callbacks机制,一种用于管理回调函数的高效工具,支持多种配置选项,如once、memory、stopOnFalse和unique。Callbacks不仅独立管理回调列表,还是Deferred异步队列的基础。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

//     Zepto.js
//     (c) 2010-2015 Thomas Fuchs
//     Zepto.js may be freely distributed under the MIT license.

/**
 * 回调函数管理:添加add() 移除remove()、触发fire()、锁定lock()、禁用disable()回调函数。它为Deferred异步队列提供支持
 * 原理:通过一个数组保存回调函数,其他方法围绕此数组进行检测和操作
 *
 *
 *  标记:
 *      once: 回调只能触发一次
 *      memory 记录上一次触发回调函数列表时的参数,之后添加的函数都用这参数立即执行
 *      unique  一个回调函数只能被添加一次
 *      stopOnFalse 当某个回调函数返回false时中断执行
 */
;(function($){
  // Create a collection of callbacks to be fired in a sequence, with configurable behaviour
  // Option flags:
  //   - once: Callbacks fired at most one time.
  //   - memory: Remember the most recent context and arguments
  //   - stopOnFalse: Cease iterating over callback list
  //   - unique: Permit adding at most one instance of the same callback
  $.Callbacks = function(options) {
    options = $.extend({}, options)

    var memory, // Last fire value (for non-forgettable lists)
        fired,  // Flag to know if list was already fired    //是否回调过
        firing, // Flag to know if list is currently firing  //回调函数列表是否正在执行中
        firingStart, // First callback to fire (used internally by add and fireWith) //第一回调函数的下标
        firingLength, // End of the loop when firing   //回调函数列表长度?
        firingIndex, // Index of currently firing callback (modified by remove if needed)
        list = [], // Actual callback list     //回调数据源: 回调列表
        stack = !options.once && [], // Stack of fire calls for repeatable lists//回调只能触发一次的时候,stack永远为false

        /**
         * 回调底层函数
         */
        fire = function(data) {
          memory = options.memory && data   //记忆模式,触发过后,再添加新回调,也立即触发。
          fired = true
          firingIndex = firingStart || 0
          firingStart = 0
          firingLength = list.length
          firing = true      //标记正在回调

            //遍历回调列表
          for ( ; list && firingIndex < firingLength ; ++firingIndex ) {
              //如果 list[ firingIndex ] 为false,且stopOnFalse(中断)模式
              //list[firingIndex].apply(data[0], data[1])  这是执行回调
            if (list[firingIndex].apply(data[0], data[1]) === false && options.stopOnFalse) {
              memory = false  //中断回调执行
              break
            }
          }
          firing = false //回调执行完毕
          if (list) {
              //stack里还缓存有未执行的回调
            if (stack) stack.length && fire(stack.shift())  //执行stack里的回调
            else if (memory) list.length = 0 //memory 清空回调列表    list.length = 0清空数组的技巧
            else Callbacks.disable()             //其他情况如  once 禁用回调
          }
        },

        Callbacks = {
          //添加一个或一组到回调列表里
          add: function() {
            if (list) {        //回调列表已存在
              var start = list.length,   //位置从最后一个开始
                  add = function(args) {
                    $.each(args, function(_, arg){
                      if (typeof arg === "function") {    //是函数
                          //非unique,或者是unique,但回调列表未添加过
                        if (!options.unique || !Callbacks.has(arg)) list.push(arg)
                      }
                      //是数组/伪数组,添加,重新遍历
                      else if (arg && arg.length && typeof arg !== 'string') add(arg)
                    })
                  }

                //添加进列表
              add(arguments)

                //如果列表正在执行中,修正长度,使得新添加的回调也可以执行
              if (firing) firingLength = list.length
              else if (memory) {
                  //memory 模式下,修正开始下标,
                firingStart = start
                fire(memory)         //立即执行所有回调
              }
            }
            return this
          },

            //从回调列表里删除一个或一组回调函数
          remove: function() {
            if (list) {       //回调列表存在才可以删除
                //_作废参数
                //遍历参数
              $.each(arguments, function(_, arg){
                var index

                  //如果arg在回调列表里
                while ((index = $.inArray(arg, list, index)) > -1) {
                  list.splice(index, 1)                                //执行删除
                  // Handle firing indexes
                    //回调正在执行中
                  if (firing) {
                      //避免回调列表溢出
                    if (index <= firingLength) --firingLength  //在正执行的回调函数后,递减结尾下标
                    if (index <= firingIndex) --firingIndex     //在正执行的回调函数前,递减开始下标
                  }
                }
              })
            }
            return this
          },

            /**
             * 检查指定的回调函数是否在回调列表中
             * @param fn
             * @returns {boolean}
             */
          has: function(fn) {
              //
            return !!(list && (fn ? $.inArray(fn, list) > -1 : list.length))
          },

            /**
             * 清空回调函数
             * @returns {*}
             */
          empty: function() {
            firingLength = list.length = 0
            return this
          },

            //禁用回调函数
          disable: function() {
            list = stack = memory = undefined
            return this
          },

            /**
             * 是否已禁用回调函数
             * @returns {boolean}
             */
          disabled: function() {
            return !list
          },
            /**
             * 锁定回调函数
             * @returns {*}
             */
          lock: function() {
            stack = undefined;   //导致无法触发

             //非memory模式下,禁用列表
            if (!memory) Callbacks.disable()
            return this
          },
            /**
             * 是否是锁定的
             * @returns {boolean}
             */
          locked: function() {
            return !stack
          },

            /**
             * 用上下文、参数执行列表中的所有回调函数
             * @param context
             * @param args
             * @returns {*}
             */
          fireWith: function(context, args) {
                // 未回调过,非锁定、禁用时
            if (list && (!fired || stack)) {

              args = args || []
              args = [context, args.slice ? args.slice() : args]
              if (firing) stack.push(args)  //正在回调中  ,存入static

              else fire(args) //否则立即回调
            }
            return this
          },

            /**
             * 用参数执行列表中的所有回调函数
             * @param context
             * @param args
             * @returns {*}
             */
          fire: function() {
                //执行回调
            return Callbacks.fireWith(this, arguments)
          },

            /**
             * 回调列表是否被回调过
             * @returns {boolean}
             */
          fired: function() {
            return !!fired
          }
        }

    return Callbacks
  }
})(Zepto)

  

 

 用法

  本身单独用于管理回调函数列表。

    另外作为Deferred异步队列的基础。

     

      

 生命周期

 

 

设计原理

    

 

   设计上的疑问

         列表触发列表stack是基于,当列表正在触发函数,而又需正执行添加函数的操作。这意味着两个线程,线程A在触发fire列表,线程B往列表add函数。理论上这样设计是合理的。

        但实际上,我们分析下...

        浏览器解析页面的主要线程如下

  •     JS引擎线程
  •     GUI渲染线程 和JS互斥,原因是JS操作DOM
  •     浏览器事件触发线程
  •     计时线程
  •     HTTP请求线程  

   由于JS引擎是单线程的,任何JS的执行一个时间片段只能执行一段代码。如setTimeout,虽然开辟了计时线程,但是一旦响应时间到了,将执行JS函数时,立刻遵循单线程原则。函数塞入执行队列。事件线程也一样。一旦响应时,JS响应函数也会塞入执行队列。 问题来了,回调列表Callbacks怎么可能在触发fire的同时,再add操作呢?只有真正的多线程才能碰到这样的问题。

     Worker?Work它是无法访问本页面的$.Callbacks对象。因为它访问不了window对象。

   除非它自己再通过importScripts('zepto-callbacks.js')加载一个JS,但它和页面的Callbacks是两个不同的对象。页面向Worker发送数据,Worker加载新的Callbacks,执行完毕后,再返回数据给页面。这多么麻烦啊?(Worker用法

      所以Callbacks的这个多线程设计究竟是基于什么场景?

 

举例

此观察者案例来自  http://www.cnblogs.com/snandy/archive/2012/11/15/2770237.html

// 观察者模式
var observer = {
    hash: {},
    subscribe: function(id, callback) {
        if (typeof id !== 'string') {
            return
        }
        if (!this.hash[id]) {
            this.hash[id] = $.Callbacks()
            this.hash[id].add(callback)
        } else {
            this.hash[id].add(callback)
        }
    },
    publish: function(id) {
        if (!this.hash[id]) {
            return
        }
        this.hash[id].fire(id)
    }
}
 
// 订阅
observer.subscribe('mailArrived', function() {
    alert('来信了')
})
observer.subscribe('mailArrived', function() {
    alert('又来信了')
})
observer.subscribe('mailSend', function() {
    alert('发信成功')
})
 
// 发布
setTimeout(function() {
    observer.publish('mailArrived')
}, 5000)
setTimeout(function() {
    observer.publish('mailSend')
}, 10000)

 

转载于:https://www.cnblogs.com/mominger/p/4369469.html

基于数据挖掘的音乐推荐系统设计与实现 需要一个代码说明,不需要论文 采用python语言,django框架,mysql数据库开发 编程环境:pycharm,mysql8.0 系统分为前台+后台模式开发 网站前台: 用户注册, 登录 搜索音乐,音乐欣赏(可以在线进行播放) 用户登陆时选择相关感兴趣的音乐风格 音乐收藏 音乐推荐算法:(重点) 本课题需要大量用户行为(如播放记录、收藏列表)、音乐特征(如音频特征、歌曲元数据)等数据 (1)根据用户之间相似性或关联性,给一个用户推荐与其相似或有关联的其他用户所感兴趣的音乐; (2)根据音乐之间的相似性或关联性,给一个用户推荐与其感兴趣的音乐相似或有关联的其他音乐。 基于用户的推荐和基于物品的推荐 其中基于用户的推荐是基于用户的相似度找出相似相似用户,然后向目标用户推荐其相似用户喜欢的东西(和你类似的人也喜欢**东西); 而基于物品的推荐是基于物品的相似度找出相似的物品做推荐(喜欢该音乐的人还喜欢了**音乐); 管理员 管理员信息管理 注册用户管理,审核 音乐爬虫(爬虫方式爬取网站音乐数据) 音乐信息管理(上传歌曲MP3,以便前台播放) 音乐收藏管理 用户 用户资料修改 我的音乐收藏 完整前后端源码,部署后可正常运行! 环境说明 开发语言:python后端 python版本:3.7 数据库:mysql 5.7+ 数据库工具:Navicat11+ 开发软件:pycharm
MPU6050是一款广泛应用在无人机、机器人和运动设备中的六轴姿态传感器,它集成了三轴陀螺仪和三轴加速度计。这款传感器能够实时监测并提供设备的角速度和线性加速度数据,对于理解物体的动态运动状态至关重要。在Arduino平台上,通过特定的库文件可以方便地与MPU6050进行通信,获取并解析传感器数据。 `MPU6050.cpp`和`MPU6050.h`是Arduino库的关键组成部分。`MPU6050.h`是头文件,包含了定义传感器接口和函数声明。它定义了类`MPU6050`,该类包含了初始化传感器、读取数据等方法。例如,`begin()`函数用于设置传感器的工作模式和I2C地址,`getAcceleration()`和`getGyroscope()`则分别用于获取加速度和角速度数据。 在Arduino项目中,首先需要包含`MPU6050.h`头文件,然后创建`MPU6050`对象,并调用`begin()`函数初始化传感器。之后,可以通过循环调用`getAcceleration()`和`getGyroscope()`来不断更新传感器读数。为了处理这些原始数据,通常还需要进行校准和滤波,以消除噪声和漂移。 I2C通信协议是MPU6050与Arduino交互的基础,它是一种低引脚数的串行通信协议,允许多个设备共享一对数据线。Arduino板上的Wire库提供了I2C通信的底层支持,使得用户无需深入了解通信细节,就能方便地与MPU6050交互。 MPU6050传感器的数据包括加速度(X、Y、Z轴)和角速度(同样为X、Y、Z轴)。加速度数据可以用来计算物体的静态位置和动态运动,而角速度数据则能反映物体转动的速度。结合这两个数据,可以进一步计算出物体的姿态(如角度和角速度变化)。 在嵌入式开发领域,特别是使用STM32微控制器时,也可以找到类似的库来驱动MPU6050。STM32通常具有更强大的处理能力和更多的GPIO口,可以实现更复杂的控制算法。然而,基本的传感器操作流程和数据处理原理与Arduino平台相似。 在实际应用中,除了基本的传感器读取,还可能涉及到温度补偿、低功耗模式设置、DMP(数字运动处理器)功能的利用等高级特性。DMP可以帮助处理传感器数据,实现更高级的运动估计,减轻主控制器的计算负担。 MPU6050是一个强大的六轴传感器,广泛应用于各种需要实时运动追踪的项目中。通过 Arduino 或 STM32 的库文件,开发者可以轻松地与传感器交互,获取并处理数据,实现各种创新应用。博客和其他开源资源是学习和解决问题的重要途径,通过这些资源,开发者可以获得关于MPU6050的详细信息和实践指南
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值