宏任务 和 微任务、观察者以及模块

宏任务 和 微任务、观察者以及模块

文章目录

  • 一、宏任务 和 微任务
    • 并发模型与事件循环
    • 运行时概念
    • 事件循环
    • 宏任务 和 微任务
    • 产生宏任务的操作
    • 产生微任务的操作
  • 二、观察者
  • 三、模块
    • 从模块中导出变量
    • 导入模块
    • 模块包装器(module wrapper)


一、宏任务 和 微任务

示例:pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。

宏任务 和 微任务、观察者以及模块_第1张图片

并发模型与事件循环

JavaScript 有一个基于事件循环的并发模型,事件循环负责执行代码、收集和处理事件以及执行队列中的子任务。这个模型与其它语言中的模型截然不同,比如 C 和 Java。

运行时概念

接下来的内容解释了这个理论模型。现代 JavaScript 引擎实现并着重优化了以下描述的这些语义。
宏任务 和 微任务、观察者以及模块_第2张图片

事件循环

JavaScript中,所有的任务都可以分为

  • 同步任务:立即执行的任务,同步任务一般会直接进入到主线程中执行
  • 异步任务:异步执行的任务,比如ajax网络请求,setTimeout定时函数等
    宏任务 和 微任务、观察者以及模块_第3张图片

宏任务 和 微任务

第一个宏任务开始,直接暴露在script元素中的代码属于全局作用域。

console.log(1);

函数setTimeout本身的执行属于同步操作,但setTimeout第一个参数对应的函数属于异步任务.

setTimeout( () => console.log(2), 0 );

览器处理到script时会立即创建一个宏任务

   let p = new Promise((resolve, reject)=>{
       console.log(3);
       resolve();
   });

在当前宏任务中创建一个微任务

p.then( () => console.log(4) );

同一个script元素内属于一个宏任务,从第二个宏任务开始,在当前宏任务中创建一个微任务,并且用setTimeout注册一个宏任务。

setTimeout( () => console.log(6), 0 );

setTimeout注册的回调操作属于异步任务(宏任务)

setTimeout( add, 0, 2, 3 );

const endTime = Date.now() + 10000;

let showTime = () => {
    let datetime = new Date();
    console.log( 'macro task 3: ', datetime.toISOString() );
    if( datetime > endTime ) {
        clearInterval(timer); // 清除timer对应的定时器
    }
};

而由setInterval注册的回调操作属于异步任务(宏任务)

const timer = setInterval( showTime, 1000 );
console.log( timer );

当前任务调用 readFile 函数本身属于同步操作,但是由readFile所注册的回调操作属于异步操作(宏任务)。

fs.readFile( pathname, 'utf8', handler);

console.log('macro task 1: end');

所以可以产生宏任务的操作:

  • setTimeout注册的回调操作(即用setTimout创建的延迟任务(延迟执行一次))
  • setInterval注册的回调操作(即用setInterval创建的定时任务(周期性执行))
  • setImmediate注册的回调操作(即用setImmediate创建的延迟任务(延迟执行一次))
  • I/O操作
  • UI交互事件 (浏览器)
  • 每个script元素中全局代码都对应一个宏任务 (浏览器)

产生宏任务的操作

宏任务的时间粒度比较大,执行的时间间隔是不能精确控制的,对一些高实时性的需求就不太符合

常见的宏任务有:

  • script (可以理解为外层同步代码)

  • setTimeout/setInterval

  • UI rendering/UI事件

  • postMessageMessageChannel

  • setImmediateI/ONode.js

按照这个流程,它的执行机制是:

  • 执行一个宏任务,如果遇到微任务就将它放到微任务的事件队列中
  • 当前宏任务执行完成后,会查看微任务的事件队列,然后将里面的所有微任务依次执行完
const timer = setInterval( showTime, 1000 );
console.log( timer );

let showNames = (...names) => names.forEach( name => console.log(name) );
let showNames = (...names) => {
    console.log( `macro task 4: 输出姓名`);
    names.forEach( name => console.log(name) );
}
const immediate = setImmediate( showNames, '李某芳', '王某然', '杨某俊', '罗某丹', '秦某娜', '菲尔娜' );
console.log( immediate );

const pathname = path.resolve(__dirname,'readme.txt');
const handler = (error, content) => {
    console.log( `macro task 5: 读取文件`);
    if( error ){
        console.log(error);
        return;

产生微任务的操作

一个需要异步执行的函数,执行时机是在主函数执行结束之后、当前宏任务结束之前

常见的微任务有:

  • Promise.then
  • MutaionObserver
  • Object.observe(已废弃;Proxy 对象替代)
  • process.nextTickNode.js
console.log( 'macro task 1: begin' );

setImmediate( ()=> console.log( 'macro task 2') );

let p = Promise.resolve('\t1 micro task (fulfilled)');
p.then( result => console.log( result ) )
 .catch( reason => console.log( reason ) )
 .finally( () => console.log('\t2 micro task') );

let t = Promise.reject('\t3 micro task (rejected)');
t.then( result => console.log(result) )
 .catch( reason => console.log( reason ) )
 .finally( () => console.log('\t4 micro task') );

let divide = (a,b) => {
    console.log('\t5 micro task');
    let r = a / b;
    return r;
}

process.nextTick( divide, 100, 3 );

console.log( 'macro task 1: end' );

二、观察者

观察器的配置(需要观察什么变动):

const config = { attributes: true, childList: true, subtree: true };

创建一个观察器实例并传入回调函数

                const observer = new MutationObserver(callback);

                const click = (element, handler) =>{
                    element.addEventListener('click', handler, false);
                }

确定被观察的目标元素

                const target = document.querySelector('.target');
                const btns = document.querySelectorAll('.buttons>button');

启用观察者

                click( btns[0], evt => {
                    console.log( `宏任务:${evt.target.innerHTML}` );
                    // 开始观察目标节点
                    observer.observe( target, config );
                });

关闭观察者

                click( btns[5], evt => {
                    console.log( `宏任务:${evt.target.innerHTML}` );
                    // 停止观察
                    observer.disconnect();
                });
            })();

三、模块

node.js 环境中一个 .js 文件就是一个模块。

从模块中导出变量

宏任务 和 微任务、观察者以及模块_第4张图片
而由 匿名函数 所传入的 exportsmodule.exports 是同一个对象

console.log( exports === module.exports );

通过动态为 exports 对象 添加属性的方式导出 name 变量

exports.name = '天字一号';

通过为 exports 对象 定义属性的方式来导出 version 变量

Object.defineProperty( exports, 'version', {
    value: '壹点零',
    enumerable: true,
    configurable: false,
    writable: false
});

通过动态为 module.exports 对象 添加属性的方式导出 description 变量

module.exports.description = '这是天字一号模块壹点零版';

console.log( '[first] exports: ', exports );
console.log( '[first] module.exports: ', module.exports );

修改匿名函数的 exports 参数值,但是并没有影响 module.exports 属性值。

exports = {
    name: '天字二号',
    version: '壹点零',
    description: '这是天字二号模块的壹点零版'
}

修改 module.exports 属性值

module.exports = {
    name: '地字一号',
    version: '壹点零',
    description: '这是地字一号模块的壹点零版'
}

通过修改 module.exports 属性值的方式将整个pure对象导出

module.exports = pure;

导入模块

用 require 函数导入模块,如果导入的模块是 node.js 内置模块,则在参数中仅指定模块名称即可

require 是 node 用来加载并执行其它文件导出的模块的方法。
在 NodeJs 中,我们引入的任何一个模块都对应一个 Module 实例,包括入口文件。

const path = require('path');

若导入的模块是自定义模块,则需要显式指定模块路径和名称

const first = require('./first.js');
const second = require('./second.js');

模块包装器(module wrapper)

每个文件中的所有源代码都被 module wrapper 包裹到一个函数中,在 node.js 调用 module wrapper 之前会创建一个表示该模块的对象(即通过参数传入的 module 实例)
宏任务 和 微任务、观察者以及模块_第5张图片

console.log('exports: ', exports ); // module wrapper 的 第 1 个参数

console.log('require函数: ');
console.log( require.toString() ); // module wrapper 的 第 2 个参数

console.log('module: ', module ); // module wrapper 的 第 3 个参数

console.log('文件路径: ', __filename); // module wrapper 的 第 4 个参数

console.log('文件目录: ', __dirname); // module wrapper 的 第 5 个参数

你可能感兴趣的:(java,算法,jvm,开发语言,pycharm)