异步模块模式

简介

  众所周知,模块化开是会将复杂的系统分解成高内聚、低耦合的模块,使系统开发变得可控、可维护、可拓展,提高模块复用率。而在js中,异步模块模式的情况则比较多,所谓异步模块模式,是在请求发出后,继续其他业务逻辑,直到模块加载完成后执行后续的逻辑,实现模块开发中对模块加载完成后的引用。

  今天就来分析一下异步加载模块,本文通过创建与调度模块、加载模块和设置模块三个方面来分析

创建与调度模块

  创建与调度方法集模块创建方法于一身。在这个方法中腰遍历所有依赖模块,并判断所有模块都存在才可执行回调函数,否则加载相应文件,直到文件加载完成才执行回调函数。

实现代码如下:

/**
     * 创建或调用模块方法
     * @param url           模块url
     * @param modDeps       依赖模块
     * @param modCallback   模块主函数
     */
    F.module = function(url, modDeps, modCallback) {
        // 将参数转化为数组
        let args = [].slice.call(arguments);
        // 获取模块构造函数(参数数组中最后一个参数成员)
        let callback = args.pop();
        // 获取依赖模块(紧邻回调函数参数,且数据类型为数组)
        let deps = (args.length && args[args.length - 1] instanceof Array) ? args.pop() : [];
        // 该模块url(模块ID)
        url = args.length ? args.pop() : null;
        //  依赖模块序列
        let params = [];
        // 未加载的依赖模块数量统计
        let depsCount = 0;
        // 依赖模块序列中索引值
        let i = 0;
        // 依赖模块序列长度
        let len;

        if(len = deps.length) {
            while(i < len) {
                (function(i) {
                    // 增加未加载依赖模块数量统计
                    depsCount++;
                    // 异步加载依赖模块
                    loadModule(deps[i], function(mod) {
                        // 依赖模块序列中添加依赖模块数量统一减一
                        depsCount--;
                        params[i] = mod;
                        // 如果依赖模块全部加载
                        if(depsCount === 0) {
                            // 在模块缓存器中矫正该模块,并执行构造函数
                            setModule(url, params, callback);
                        }
                    });
                })(i);
                // 遍历下一个模块
                i++;
            }
            // 无依赖模块,直接执行回调函数
        } else {
            // 在模块缓存器中矫正该模块,并执行构造函数
            setModule(url, [], callback);
        }
    }

在module方法中有两个方法还没有定义:loadModule(加载模块)和setModule(设置模块),接下来,便一一介绍

加载模块

loadModule方法目的是加载依赖模块对应的文件并执行回调函数。对此可分三种情况处理:

  1. 如果文件已经被要求加载过,我们要区分文件已经加载完成或是正在加载中,如果已经完成,我们异步执行该模块的加载完成回调函数(相见F.module方法中对loadModule方法调用部分)
  2. 如果文件未加载完成,我们要将加载完成回调函数缓存入模块加载完成回调函数容器中(该模块的onload数组容器)。
  3. 如果依赖模块对应的文件未被要求加载过,那么我们要加载该文件,并将该依赖模块的初始化信息写入模块缓存器中

代码如下:

 // 模块缓存器。存储已创建模块
    let moduleCache = {};
 /**
     * 异步加载依赖模块所在文件
     * @param moduleName        模块路径(id)
     * @param callback          模块加载完成回调函数
     */
    function loadModule(moduleName, callback) {
        let _module;
        // 如果依赖模块被要求加载过
        if(moduleCache[moduleName]) {
            _module = moduleCache[moduleName];
            // 如果模块加载完成
            if(_module.status === ‘loaded‘) {
                // 执行模块加载完成后回调函数
                setTimeout(callback(_module.exports), 0);
            } else {
                // 缓存该模块所处文件加载完成回调函数
                _module.onload.push(callback);
            }
            // 模块第一次被依赖引用
        } else {
            // 缓存该模块初始化信息
            moduleCache[moduleName] = {
                // 模块ID
                moduleName: moduleName,
                // 模块对应文件加载状态(默认加载中)
                status: ‘loading‘,
                // 模块接口
                exports: null,
                // 模块对应文件加载完成回调函数缓冲器
                onload: [callback]
            };
            // 加载模块对应文件
            loadScript(getUrl(moduleName), moduleName);
        }
    }

loadModule方法中在加载模块对应文件时需要引用loadScript加载脚本方法和getUrl获取文件路径方法。这两个方法等实现如下:

function getUrl(moduleName) {
        // 拼接完整的文件路径字符串,如‘lib/ajax‘ => ‘lib/ajax.js‘
        return String(moduleName).replace(/\.js$/g, ‘‘) + ‘.js‘;
    }

    function loadScript(src, id) {
        let _script = document.createElement(‘script‘);
        // 文件类型
        _script.type = ‘text/JavaScript‘;
        // 确认编码
        _script.charset = ‘UTF-8‘;
        // 异步加载
        _script.async = true;
        // 文件路径
        _script.src = src;
        // 文件id
        _script.id = id;
        document.getElementsByTagName(‘head‘)[0].appendChild(_script);
    }

  由getUrl方法可以看出,模块的id和文件的路径应该是一一对应的关系,不然在解析路径时就会出错。不过,也可以将getUrl改造成你喜欢的格式,在这里就不多说了。

设置模块

表面上看设置模块就是执行回调函数的,但实质上它做了三件事:

  1. 对创建的模块来说,当我的所有依赖模块加载完成时,我要使用该方法;
  2. 对应被依赖的模块来说,其所在的文件加载后要执行该依赖模块(即创建该模块过程)又间接地使用该方法;
  3. 对于一个匿名函数来说(F.module方法中无URL参数数据),执行过程中也会使用该方法;

设置模块代码如下:

/**
     * 设置模块并执行模块构造函数
     * @param moduleName        模块ID名称
     * @param params            依赖模块
     * @param callback          模块构造函数
     */
    function setModule(moduleName, params, callback) {
        let fn;
        // 如果模块被调用过
        if(moduleCache[moduleName]) {
            let _module = moduleCache[moduleName];
            // 设置模块已经加载完成
            _module.status = ‘loaded‘;
            // 矫正模块接口
            _module.exports = callback ? callback.apply(_module, params) : null;
            // 执行模块文件加载完成回调函数
            while(fn = _module.onload.shift()) {
                fn(_module.exports);
            }
        } else {
            // 模块不存在(匿名模块),则直接执行构造函数
            callback && callback.apply(null, params);
        }
        // 删除加载的script标签
        deleteScript(moduleName);
    }

在设置代码执行后,便将加载到head中的script标签删除,纯属个人代码洁癖,可以没有的,删除标签代码如下:

function deleteScript(id) {
        const deleteJs = document.getElementById(id);
        console.log(deleteJs);
        if(deleteJs) {
            document.getElementsByTagName(‘head‘)[0].removeChild(deleteJs);
        }
    }

最后

  异步模块模式不仅减少了多人开发过程中变量、方法名被覆盖的问题,而且增加了模块依赖,使开发者不必担心某些方法尚未加载或未加载完成造成的无法使用问题。异步加载部分功能也可以将更多首屏不必要的功能剥离出去,减少首屏加载成本。

demo可以在此看到:

https://github.com/weiruifeng/async

时间: 2024-10-10 04:24:29

异步模块模式的相关文章

AMD异步模块定义介绍和Require.js中使用jQuery及jQuery插件的方法

AMD 模块 AMD(异步模块定义,Asynchronous Module Definition)格式总体的目标是为现在的开发者提供一个可用的模块化 JavaScript 的解决方案. AMD 模块格式本身是一个关于如何定义模块的提案,在这种定义下模块和依赖项都能够异步地进行加载.它有很多独特的优势,包括天生的异步及高度灵活等特性,这些特性能够解除常见的代码与模块标识间的那种紧密耦合.目前它已经被很多项目所接纳,包括jQuery(1.7). RequireJS RequireJS是一个工具库,主

深入理解JavaScript 模块模式

模块模式是JavaScript一种常用的编码模式.这是一般的理解,但也有一些高级应用没有得到很多关注.在本文中,我将回顾基础知识,浏览一些不错的高级技巧,甚至我认为是原生基础的. 基础知识 首先我们开始简单概述模型模式.三年前Eric Miraglia(YUI)的博文使模型模式众所周知.如果你已经很熟悉模型模式,可以直接阅读“高级模式”. 匿名闭包 这是一切成为可能的基础,也是JavaScript最好的特性.我们将简单的创建匿名函数,并立即执行.所有函数内部代码都在闭包(closure)内.它提

深入理解JavaScript&#160;模块模式

模块模式是JavaScript一种常用的编码模式.这是一般的理解,但也有一些高级应用 没有得到很多关注.在本文中,我将回顾基础知识,浏览一些不错的高级技巧,甚至我认为是原生基础的. 基础知识首先我们开始简单概述模型模式.三年前Eric Miraglia(YUI)的博文使模型模式众所周知.如果你已经很熟悉模型模式,可以直接阅读“高级模式”.匿名闭包这是一切成为可能的基础,也是JavaScript最好的特性.我们将简单的创建匿名函数,并立即执行.所有函数内部代码都在闭包(closure)内.它提供了

(转)深入理解JavaScript 模块模式

深入理解JavaScript 模块模式 (原文)http://www.cnblogs.com/starweb/archive/2013/02/17/2914023.html 英文:http://www.adequatelygood.com/2010/3/JavaScript-Module-Pattern-In-Depth 模块模式是JavaScript一种常用的编码模式.这是一般的理解,但也有一些高级应用没有得到很多关注.在本文中,我将回顾基础知识,浏览一些不错的高级技巧,甚至我认为是原生基础的

asyncio异步模块

线程池实现爬取数据 import requests import time from multiprocessing.dummy import Pool urls = [ "http://127.0.0.1:5000/zhou1", "http://127.0.0.1:5000/zhou2", "http://127.0.0.1:5000/zhou3"] def get_request(url): response = requests.get(

在服务器端采用同步处理模式和异步处理模式的分析

同步服务为每个请求创建单一线程,由此线程完成请求任务:接收消息,处理消息,返回数据:这种情况下服务器资源对所有入栈请求开放,服务器资源被所有入栈请求竞争使用,如果入栈请求过多就会导致服务器资源耗尽宕机,或者导致竞争加剧,资源调度频繁,服务器资源利用效率降低. 异步服务则可以分别设置两个线程队列,一个专门负责接收消息,另一个专门负责处理消息并返回数据,另有一些值守线程负责任务派发和超时监控等工作.在这种情况下无论入栈请求有多少,服务器始终依照自己的能力处理请求,服务器资源消耗始终在一个可控的范围.

模块模式

模块模式: 在 JavaScript 中,Module模块用于模拟类的概念,使一个单独的对象拥有共有/私有方法和变量,从而屏蔽来自全局作用域的特殊部分.(产生的结果是:函数名与在页面上其他脚步定义的函数冲突的可能性降低) 涉及: 对象字面量: 闭包: 作用域. 实现的方法: 对象字面量表示法: Module 模式: AMD 模式: CommonJS 模式: ECMAScript Harmony 模块. 优点: 提供一个包装混合公有/私有方法和变量的方式,防止其泄露至全局作用域,与其他接口发生冲突

AFHTTPClient的异步回调模式

以前第一个版本,ios的http都用的同步模式,在很多地方会导致线程阻塞,自己开发了一个简易的AFHTTPClient的异步回调模式. 回调的protocol: @protocol MyAFNetworkingResponse <NSObject> @required -(void) MyHttpResponse:(NSString*)ret Type:(NSString*)strType returnData:(NSObject*)retData; @end AFHTTPClient的异步通

.NET应用架构设计—表模块模式与事务脚本模式的代码编写

阅读目录: 1.背景介绍 2.简单介绍表模块模式.事务脚本模式 3.正确的编写表模块模式.事务脚本模式的代码 4.总结 1.背景介绍 要想正确的设计系统架构就必须能正确的搞懂每个架构模式的用意,而不是胡子眉毛一把抓.现在有一个现象是什么呢,项目的结构从表面上看是很不错,层分的很合理,其实对业务系统来说也就那么几种层设计方法,但是现在很多项目的逻辑架构的设计不是理想,有很多概念大家并不是很了解,当然也许每个人对技术的追求不同罢了.不管你追求不追求,事实我们还是要去往正确的方向努力才对的. 很多人包