nodejs事件循环与多进程(一)——事件循环允许Node.js执行非阻塞IO操作 & js是操作DOM,决定了单线程 & 事件循环之宏任务setTimeout在后、微任务promise在前

nodejs事件循环与多进程(一)——事件循环允许Node.js执行非阻塞I/O操作 & js是操作DOM,决定了单线程 & 事件循环之宏任务setTimeout在后、微任务promise在前

nodejs事件循环与多进程

why

  • 事件循环对于深入理解nodejs异步至关重要
    • fs, net,http,events
  • 事件循环是企业面试中的最高频考题之一
  • 能驾驭nodejs多进程是一名资深前端工程师的标志

课程介绍

  • 了解事件循环的概念
  • 学习浏览器中的事件循环机制
  • 学习nodejs中的事件循环机制
  • 了解多进程,多线程之间的区别
  • 学习nodejs中的多进程并使用cluster来开启多进程

学习目标

  • 深入掌握浏览器与nodejs中的事件循环机制,并且能理解它们之间的区别
  • 使用cluster开启多进程

第一章 事件循环介绍

浏览器中的事件循环

为了协调事件(event),用户交互(user interaction),脚本(script),渲染(rendering),网络(networking)等,用户代理(user agent)必须使用事件循环(event loops)。

To coordinate events, user interaction, scripts, rendering, networking, and so forth, user agents must use event loops as described in this section. Each agent has an associated event loop.

  • 事件:PostMessage, MutationObserver等
  • 用户交互: click, onScroll等
  • 渲染: 解析dom,css等
  • 脚本:js脚本执行

nodejs中的事件循环

事件循环允许Node.js执行非阻塞I / O操作 - 尽管JavaScript是单线程的 - 通过尽可能将操作卸载到系统内核。
由于大多数现代内核都是多线程的,因此它们可以处理在后台执行的多个操作。当其中一个操作完成时,内核会告诉Node.js,以便可以将相应的回调添加到轮询队列中以最终执行。

The event loop is what allows Node.js to perform non-blocking I/O operations — despite the fact that JavaScript is single-threaded — by offloading operations to the system kernel whenever possible.
Since most modern kernels are multi-threaded, they can handle multiple operations executing in the background. When one of these operations completes, the kernel tells Node.js so that the appropriate callback may be added to the poll queue to eventually be executed. We’ll explain this in further detail later in this topic.

  • 事件: EventEmitter
  • 非阻塞I / O:网络请求,文件读写等
  • 脚本:js脚本执行

事件循环的本质

在浏览器或者nodejs环境中,运行时对js脚本的调度方式就叫做事件循环。

  • promise在前,setTimeout在后
setTimeout(() => {
    
    
  console.log('setTimeout')
}, 0);

Promise.resolve().then(() => {
    
    
  console.log('promise');
});

console.log('main');

// 执行顺序是   1. main 2. promise 3. setTimeout

第二章 浏览器事件循环

Javascript为什么是单线程的?

浏览器js的作用是操作DOM,这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?

任务队列

单线程就意味着所有任务需要排队,如果因为任务cpu计算量大还好,但是I/O操作cpu是闲着的。所以js就设计成了一门异步的语言,不会做无畏的等待。任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。

(1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。

(2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。

(3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。

(4)主线程不断重复上面的第三步。

setTimeout(() => {
    
    
  console.log('setTimeout')
}, 0);

console.log('main1');
console.log('main2');
// 执行顺序是   1. main1 2. main2 3. setTimeout

主线程从"任务队列"中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)。

宏任务与微任务

除了广义的同步任务和异步任务,JavaScript 单线程中的任务可以细分为宏任务(macrotask)微任务(microtask)

  • macrotask: script(整体代码), setTimeout, setInterval, setImmediate, I/O, UI rendering。
  • microtask:process.nextTick, Promise, Object.observe, MutationObserver。
  1. 宏任务进入主线程,执行过程中会收集微任务加入微任务队列。
  2. 宏任务执行完成之后,立马执行微任务中的任务。微任务执行过程中将再次收集宏任务,并加入宏任务队列。
  3. 反复执行1,2步骤

在这里插入图片描述

setTimeout(() => {
    
    
  console.log('setTimeout')
}, 0);

Promise.resolve().then(() => {
    
    
  console.log('promise');
});

console.log('main');

// 1. main 2. promise 3. setTimeout

执行机制

在这里插入图片描述

高频面试题

setTimeout(() => {
    
    
  console.log('setTimeout');
}, 0);

Promise.resolve().then(() => {
    
    
  console.log('promise');
  Promise.resolve().then(() => {
    
    
    console.log('promise2');
  });
});

console.log('main');
// 执行顺序是  main、promise、promise2、setTimeout

每轮事件循环执行一个宏任务和所有的微任务。

嵌套执行顺序

在这里插入图片描述

setTimeout(() => {
    
    
  Promise.resolve().then(() => {
    
    
    console.log('promise');
  });
}, 0);

Promise.resolve().then(() => {
    
    
  setTimeout(() => {
    
    
  	console.log('setTimeout');
  }, 0);
});

console.log('main');

// 打印顺序是 —— 1. main 2. promise 3. setTimeout
// 突破口:谁先进入io,谁先出来

任务队列一定会保持先进先出的顺序执行。

交叉显示

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_44867717/article/details/131407811