关于eventloop:深入理解JavaScript之彻底弄懂JsEventLoop执行机制

JavaScript单线程起源:JavaScript作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM,为了防止复杂性,诞生开始,JavaScript就是单线程语言。比方,假设JavaScript同时有两个线程,一个线程在某个DOM节点上增加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?所以,为了防止复杂性,从一诞生,JavaScript就是单线程。 单线程及存在的问题:单线程就意味着,所有工作须要排队,前一个工作完结,才会执行后一个工作。如果前一个工作耗时很长,后一个工作就不得不始终等着。 问题的解决--同步、异步synchronous(同步工作)和asynchronousk(异步工作)同步工作是调用立刻失去后果的工作,同步工作在主线程上排队执行的工作,只有前一个工作执行结束,能力执行后一个工作;异步工作是调用无奈立刻失去后果的工作,须要额定的操作能力预期后果的工作,异步工作不进入主线程、而进入"工作队列"(task queue)的工作,只有"工作队列"告诉主线程,某个异步工作能够执行了,该工作才会进入主线程执行。macro-task(宏工作)和micro-task(微工作)宏工作:macro-task 能够了解是每次执行栈执行的代码就是一个宏工作(包含每次从事件队列中获取一个事件回调并放到执行栈中执行,每一个宏工作会从头到尾将这个工作执行结束,不会执行其它)包含整体代码script,setTimeout,setInterval等微工作:micro-task能够了解是在以后 task 执行完结后立刻执行的工作 包含Promise,process.nextTick等(未完...)

June 30, 2022 · 1 min · jiezi

关于eventloop:爪哇学习笔记宏任务微任务

JavaScript运行时在执行 JavaScript 代码的时候,JavaScript 运行时实际上保护了一组用于执行 JavaScript 代码的代理。每个代理由一组执行上下文的汇合、执行上下文栈、主线程、一组可能创立用于执行 worker 的额定的线程汇合、一个工作队列以及一个微工作队列形成。除了主线程(某些浏览器在多个代理之间共享的主线程)之外,其它组成部分对该代理都是惟一的事件循环(Event Loops)每个代理都是由事件循环驱动的,事件循环负责收集用事件(包含用户事件以及其余非用户事件等)、对工作进行排队以便在适合的时候执行回调。而后它执行所有处于期待中的 JavaScript 工作(宏工作),而后是微工作,而后在开始下一次循环之前执行一些必要的渲染和绘制操作。(本节文字来自mdn,集体感觉形容有误,具体程序往下看)Event Loops中,每一次循环成为tick,每次tick的流程如下: 执行主线程中的同步代码(能够看做一个宏工作),直到执行实现查看微工作队列,若不为空,则执行微工作队列中的所有微工作,直到队列为空查看宏工作队列中在以后tick之前退出到队列中的宏工作,顺次执行没执行完一个宏工作,反复顺次第二个步骤当微工作队列已清空且宏工作队列中以后tick之前退出的宏工作都已执行实现,执行一些别要的渲染和绘制工作(宿主环境为浏览器时)完结以后tick,进入下一个tick分类Window 事件循环Worker 事件循环Worklet 事件循环宏工作&微工作一个工作(宏工作)就是指打算由规范机制来执行的任何 JavaScript,如程序的初始化、事件触发的回调等。 除了应用事件,你还能够应用 setTimeout() 或者 setInterval() 来增加工作。起初微工作和工作之间的差别看起来不大。它们很类似;都由位于某个队列的 JavaScript 代码组成并在适合的时候运行。宏工作和微工作的区别当执行来自工作队列中的工作时,在每一次新的事件循环开始迭代的时候运行时都会执行队列中的每个工作。在每次迭代开始之后退出到队列中的工作须要在下一次迭代开始之后才会被执行.每次当一个工作退出且执行上下文为空的时候,微工作队列中的每一个微工作会顺次被执行。不同的是它会等到微工作队列为空才会进行执行——即便中途有微工作退出。换句话说,微工作能够增加新的微工作到队列中,并在下一个工作开始执行之前且以后事件循环完结之前执行完所有的微工作。创立路径宏工作: 程序的初始化事件触发的回调setTimeout()/setInterval()postMessage, MessageChannelsetImmediate() node环境微工作: PromiseMutaionObserverObject.observe(已废除,被Proxy对象代替)process.nextTick() node环境queueMicrotask()代码示例setTimeout(() => { console.log(3); // 在执行宏工作时退出宏工作,退出的宏工作会在下个tick执行 setTimeout(() => { console.log(8); }); // 在执行宏工作时退出微工作,以后宏工作执行实现后,立刻执行该微工作 new Promise(resolve => { resolve(); console.log(4); }).then(() => { console.log(6); }); // 在执行宏工作时退出宏工作,退出的宏工作会在下个tick执行 setTimeout(() => { console.log(9); }); console.log(5);});setTimeout(() => { console.log(7);});new Promise(resolve => { resolve(); console.log(1);}).then(() => { console.log(2);});// 输入后果:// 1// 2// 3// 4// 5// 6// 7// 8// 9参考文档: ...

January 17, 2022 · 1 min · jiezi

关于eventloop:前端笔记杂项二

宏工作与微工作宏工作 每次执行栈执行的代码就是一个宏工作(包含每次从事件队列中获取一个事件回调并放到执行栈中执行)。浏览器为了可能使得JavaScript外部宏工作与DOM工作可能有序的执行,**会在一个宏工作执行完结后,在下一个宏工作执行开始前,对页面进行从新渲染**。宏工作包含:**script(整体代码)、setTimeout、setInterval、I/O、UI交互事件、postMessage、MessageChannel、setImmediate(Node.js 环境)**微工作 **能够了解是在以后 task 执行完结后立刻执行的工作**。也就是说,在以后task工作后,下一个task之前,在渲染之前。微工作的响应速度相比宏工作会更快,因为无需等渲染。也就是说,在某一个宏工作执行完后,就会将在它执行期间产生的所有微工作都执行结束(在渲染前)。微工作次要蕴含:**Promise.then、MutaionObserver、process.nextTick(Node.js 环境)**Set向 Set 退出值的时候,不会产生类型转换,所以5和"5"是两个不同的值 扩大运算符(...)外部应用for...of循环 WeakSet * WeakSet 的成员只能是对象,而不能是其余类型的值。 * WeakSet 中的对象都是弱援用,即垃圾回收机制不思考 WeakSet 对该对象的援用,也就是说,如果其余对象都不再援用该对象,那么垃圾回收机制会主动回收该对象所占用的内存,不思考该对象还存在于 WeakSet 之中 MapObject 构造提供了“字符串—值”的对应,Map 构造提供了“值—值”的对应,是一种更欠缺的 Hash 构造实现 React HooksuseState * `setState` 函数用于更新 state。它接管一个新的 state 值并将组件的一次从新渲染退出队列。在后续的从新渲染中,`useState` 返回的第一个值将始终是更新后最新的 state。 * 惰性初始 state:`initialState` 参数只会在组件的初始渲染中起作用,后续渲染时会被疏忽。如果初始 state 须要通过简单计算取得,则能够传入一个函数,在函数中计算并返回初始的 state,此函数只在初始渲染时被调用 * 跳过 state 更新:调用 State Hook 的更新函数并传入**以后**的 state 时,React 将跳过子组件的渲染及 effect 的执行。(React 应用 [`Object.is` 比拟算法](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is#Description) 来比拟 state。) * `useState` 不会主动合并更新对象: setValue(prevState => { // 也能够应用 Object.assign return {...prevState, ...updatedValues}; }); useEffect ...

April 9, 2021 · 1 min · jiezi

关于eventloop:从event-loop规范探究javaScript异步及浏览器更新渲染时机

异步的思考event loops暗藏得比拟深,很多人对它很生疏。但提起异步,置信每个人都晓得。异步背地的“靠山”就是event loops。这里的异步精确的说应该叫浏览器的event loops或者说是javaScript运行环境的event loops,因为ECMAScript中没有event loops,event loops是在HTML Standard定义的。 event loops标准中定义了浏览器何时进行渲染更新,理解它有助于性能优化。 思考下边的代码运行程序: console.log('script start');setTimeout(function () { console.log('setTimeout');}, 0);Promise.resolve() .then(function () { console.log('promise1'); }) .then(function () { console.log('promise2'); });console.log('script end'); 下面的程序是在chrome运行得出的,乏味的是在safari 9.1.2中测试,promise1 promise2会在setTimeout的后边,而在safari 10.0.1中失去了和chrome一样的后果。为何浏览器有不同的体现,理解tasks, microtasks队列就能够解答这个问题。 很多框架和库都会应用相似上面函数: function flush() {...}function useMutationObserver() { var iterations = 0; var observer = new MutationObserver(flush); var node = document.createTextNode(''); observer.observe(node, { characterData: true }); return function () { node.data = iterations = ++iterations % 2; };}首次看这个useMutationObserver函数总会很有纳闷,MutationObserver不是用来察看dom的变动的吗,这样凭空造出一个节点来重复批改它的内容,来触发察看的回调函数有何意义? ...

January 14, 2021 · 5 min · jiezi

发布高性能-Go-网络库-gnet-发布-v1-版

Github 主页https://github.com/panjf2000/... 欢迎大家围观~~,目前还在持续更新,感兴趣的话可以 star 一下暗中观察哦。 简介gnet 是一个基于事件驱动的高性能和轻量级网络框架。它直接使用 epoll 和 kqueue 系统调用而非标准 Golang 网络包:net 来构建网络应用,它的工作原理类似两个开源的网络库:netty 和 libuv。 这个项目存在的价值是提供一个在网络包处理方面能和 Redis、Haproxy 这两个项目具有相近性能的 Go 语言网络服务器框架。 gnet 的亮点在于它是一个高性能、轻量级、非阻塞的纯 Go 实现的传输层(TCP/UDP/Unix-Socket)网络框架,开发者可以使用 gnet 来实现自己的应用层网络协议,从而构建出自己的应用层网络应用:比如在 gnet 上实现 HTTP 协议就可以创建出一个 HTTP 服务器 或者 Web 开发框架,实现 Redis 协议就可以创建出自己的 Redis 服务器等等。 gnet 衍生自另一个项目:evio,但性能远胜之。 功能高性能 的基于多线程/Go程模型的 event-loop 事件驱动内置 Round-Robin 轮询负载均衡算法内置 goroutine 池,由开源库 ants 提供支持内置 bytes 内存池,由开源库 pool 提供支持简洁的 APIs基于 Ring-Buffer 的高效内存利用支持多种网络协议:TCP、UDP、Unix Sockets支持两种事件驱动机制:Linux 里的 epoll 以及 FreeBSD 里的 kqueue支持异步写操作灵活的事件定时器SO_REUSEPORT 端口重用核心设计多线程/Go程模型主从多 Reactors 模型gnet 重新设计开发了一个新内置的多线程/Go程模型:『主从多 Reactors』,这也是 netty 默认的线程模型,下面是这个模型的原理图: ...

October 15, 2019 · 3 min · jiezi

Nodejs-中的事件循环计时器和processnextTick

前言本篇文章翻译自 Node.js 官网的同名文章也算是经典老物了, 不过官网的文章也随着 Node.js 的演化在修改, 这篇文章最后的编辑时间是 2019年9月10日请注意时效性, 地址在文章的最后有给出. 首次翻译英语水平有限, 错误之处还请多多指教. 什么是事件循环事件循环允许node.js执行非阻塞I/O操作. 虽然 JavaScript 是单线程的, 但是事件循环会尽可能的将操作转移到系统内核中来完成. 现代的操作系统内核都是多线程的, 它们可以在后台处理多种操作. 一旦这些操作完成, 系统内核会通知 Node.js 以便将事件回调放入轮询队列中等待执行. (我们会在随后的内容讨论它们的具体工作细节) 解析事件循环当 Node.js 启动的时候, 他会初始化事件循环, 处理输入的脚本内容 (或者进入 REPL), 脚本可能会调用异步接口, 设置定时器, 或者调用 process.nextTick(), 然后开始处理事件循环(eventloop). 下面的简图中展示了事件循环的操作流程: ┌───────────────────────┐┌─>│ timers ││ └──────────┬────────────┘│ ┌──────────┴────────────┐│ │ I/O callbacks ││ └──────────┬────────────┘│ ┌──────────┴────────────┐│ │ idle, prepare ││ └──────────┬────────────┘ ┌───────────────┐│ ┌──────────┴────────────┐ │ incoming: ││ │ poll │<─────┤ connections, ││ └──────────┬────────────┘ │ data, etc. ││ ┌──────────┴────────────┐ └───────────────┘│ │ check ││ └──────────┬────────────┘│ ┌──────────┴────────────┐└──┤ close callbacks │ └───────────────────────┘每一个方框代表了事件循环中不同的阶段(所有阶段执行完成算是一次事件循环).每一个阶段都有一个由回调组成的 FIFO 队列被用于执行. 虽然不同的队列执行方式不同, 总的来看, 当事件循环进入该阶段后会执行该阶段对应的操作, 然后调用对应的回调直到队列耗尽或者达到了回调执行上限. 在到达上述情况后事件循环进入下一阶段, 然后继续这样的流程. ...

September 11, 2019 · 4 min · jiezi

前端培训中级阶段2-事件event-事件冒泡捕获-20190620期

前端最基础的就是 HTML+CSS+Javascript。掌握了这三门技术就算入门,但也仅仅是入门,现在前端开发的定义已经远远不止这些。前端小课堂(HTML/CSS/JS),本着提升技术水平,打牢基础知识的中心思想,我们开课啦(每周四)。 前面我们已经基本掌握常规的语法语义,以及基本的使用方法。接下来我们讲深入进去了解其中内在的原理。 今天我们要讲什么?事件机制事件对象(Event)event loopDOM (与事件的关系,看不看无所谓)DOM(Document Object Model——文档对象模型)是用来呈现以及与任意 HTML 或 XML文档交互的 API。DOM 是载入到浏览器中的文档模型,以节点树的形式来表现文档,每个节点代表文档的构成部分(例如:页面元素、字符串或注释等等)。DOM 是万维网上使用最为广泛的 API 之一,因为它允许运行在浏览器中的代码访问文件中的节点并与之交互。节点可以被创建,移动或修改。事件监听器可以被添加到节点上并在给定事件发生时触发。DOM 并不是天生就被规范好了的,它是浏览器开始实现JavaScript时才出现的。这个传统的 DOM 有时会被称为 DOM 0。现在, WHATWG 维护DOM现存标准。-- MDN既然 DOM 有版本,那么在他的环境上事件的支持也是有版本的。文档 DOM 事件(0 级)body.onclick 这种定义方式的。 不可以多次监听事件,因为是赋值的方式,下次赋值会覆盖。只可以在冒泡阶段触发DOM 事件(2 级)addEventListener 方式定义的。 可以多次监听,切按监听顺序执行回调(有序)取消监听需要同一引用的函数。举个栗子 // 错误案例,两个方法不是同一引用,导致清除不掉 document.addEventListener('click', function(){}) document.removeEventListener('click', function(){}) // 正确案例,同一引用,可以清除。 function documentClick(){} document.addEventListener('click', documentClick) document.removeEventListener('click', documentClick) 可以选择触发阶段(冒泡&捕获) capture事件机制标准事件:EMCAScript 标准规定事件流包含三个阶段,分别为事件捕获阶段,目标阶段,事件冒泡阶段。先存个代码,之后的例子我们用这个例子。测试看我这里的 DEMO <html onclick="alert('html')"> <body onclick="alert('body')"> <a onclick="alert('a')">click</a> </body></html>事件捕获阶段捕获阶段:由外到内,触发规律为 html > body > a。如果想在捕获阶段就触发,需要传入参数 {capture: true} 事件冒泡阶段冒泡阶段:由内到外,触发规律为 a > body > html这个阶段执行是 W3C 默认的,等价于 {capture: false} ...

June 18, 2019 · 2 min · jiezi

浅析-JS-事件循环之-Microtask-和-Macrotask

简介我们在上一篇 《浅析 JS 中的EventLoop 事件循环》 中提到一个 Event Queue,其实在事件循环中 queue 一共有两种,还有一种叫 Job Queue 其中 Event Queue 在 HTML 规范中被称为 Task Queue,但是为了区分,一般都叫作 Macrotask QueueJob Queue 是在 ECMAScript 规范中谈及处理 Promise 回调时提到的,但是由于和 V8 中的实现比较相似,所以一般都称为 Microtask QueueMacrotaskMacrotasks 包含了解析 HTML、生成 DOM、执行主线程 JS 代码和其他事件如 页面加载、输入、网络事件、定时器事件等。从浏览器的角度,Macrotask 代表的是一些离散的独立的工作。 常见应用setTimeout, setInterval, setImmediate, requestAnimationFrame, I/O, UI rendering MicrotaskMicrotasks 则是为了完成一些更新应用程序状态的较小的任务,如处理 Promise 的回调和 DOM 的修改,以便让这些任务在浏览器重新渲染之前执行。Microtask 应该以异步的方式尽快执行,所以它们的开销比 Macrotask 要小,并且可以使我们在 UI 重新渲染之前执行,避免了不必要的 UI 渲染。 常见应用process.nextTick, Promises, Object.observe, MutationObserver 执行顺序Event Loop 的实现需要至少一个 Macrotask Queue 和至少一个 Microtask Queue。为了便于理解,我们都简化成一个。简单来说,Microtask Queue 具有更高的优先级,即执行一个 Macrotask 任务后,就会清空整个 Microtask Queue,此时如果有新的 Microtask 加入也会被执行。 ...

June 7, 2019 · 1 min · jiezi

从js来聊聊异步编程

文章的目的揭开go的 gorouter,c#的 async/await等 使用同步的写法写异步代码的神秘面纱 , 证明其本质就是一个语法糖 为什么使用js来讲异步编程因为js可以通过编程语言自己的语法特性,实现async/await语法 js异步最底层写法promiseconst promise = new Promise(function(resolve, reject) { xxxxx.异步IO操作((res)=>{ if(res成功){ resolve(res) }else{ reject(res) } })});promise出入的回调函数有一定的要求 resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数(处理返回的结果)。promise.then(function(value) { // success}, function(error) { // failure});引申-注意: promise对象在js中非常特殊,比如下面的例子const p1 = new Promise(function (resolve, reject) { setTimeout(() => reject(new Error('fail')), 3000)})const p2 = new Promise(function (resolve, reject) { setTimeout(() => resolve(p1), 1000)})p2 .then(result => console.log(result)) .catch(error => console.log(error))这个的结果是failt 因为 p2中resolve返回一个promise对象,这个操作将会导致p2的状态升级成p1的状态(标准)promise的then链式写法promise then方法将会返回一个promise,所以js支持链式异步 ...

May 31, 2019 · 3 min · jiezi

浅析-JS-中的-EventLoop-事件循环新手向

序Event Loop 这个概念相信大家或多或少都了解过,但是有一次被一个小伙伴问到它具体的原理的时候,感觉自己只知道个大概印象,于是计划着写一篇文章,用输出倒逼输入,让自己重新学习这个概念,同时也能帮助更多的人理解它~ 概念JavaScript 是一门 单线程 语言,即同一时间只能执行一个任务,即代码执行是同步并且阻塞的。 eg. 这就像只有一个窗口的银行,客户需要一个一个排队办理业务。只能同步执行肯定是有问题的,所以 JS 有了一个用来实现异步的函数:setTimeout 下面要讲的 Event Loop 就是为了确保 异步代码 可以在 同步代码 执行后继续执行的。 由于涉及到的相关概念较多,我们先从最简单的来。 队列(Queue)队列 是一种 FIFO(First In, First Out) 的数据结构,它的特点就是 先进先出 eg. 生活中最常见的例子就是排队啦,排在队伍最前面的人最先被提供服务。栈(Stack)栈 是一种 LIFO(Last In, First Out)的数据结构,特点即 后进先出。 eg. 大家都吃过桶装薯片吧~薯片在包装的时候只能从顶部放入,而吃的时候也只能从顶部拿出,这就叫后进先出哈调用栈(Call Stack)栈我们已经知道了,那么什么是 调用栈 呢 ? 它本质上当然还是个栈啦 废话,关键在于它里面装的东西,是一个个待执行的函数。 Event Loop 会一直检查 Call Stack 中是否有函数需要执行,如果有,就从栈顶依次执行。同时,如果执行的过程中发现其他函数,继续入栈然后执行。先拿两个函数来说: 栈空现在执行到一个 函数A,函数A 入栈函数A 又调用了 函数B,函数B 入栈函数B 执行完后 出栈然后继续执行 函数A,执行完后A也 出栈栈空更复杂一点的话,来看一段代码: 这段代码在 调用栈中的运行顺序如下图: 这个调用栈其实大家经常会见到,就是在控制台报错的时候,错误信息显示的就是当前时刻调用栈的状态。But, 上面我们讨论的其实都是同步代码,代码在运行的时候只用 调用栈 解释就可以了。 ...

May 28, 2019 · 1 min · jiezi