前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >并发模型与事件循环

并发模型与事件循环

作者头像
gojam
发布2019-09-09 15:59:08
7550
发布2019-09-09 15:59:08
举报
文章被收录于专栏:gojam技术备忘录

JavaScript进阶

#包管理器

#NPM

默认安装到项目目录下,-g安装到全局,-save在package.json写入dependencies字段,-save-dev相应写入devDependencies字段。

#YARN

yarn add添加包,yarn global add添加全局包,yarn add --dev添加dev依赖。yarn添加的依赖会默认保存到package.json里。

#import与require

import与require都提供引入一个模块的功能,但require是AMD规范下的引入,在运行时调用,而import是ES6规定的引入,编译时调用(因此实际上最早执行,)。require对应exports,import对应export。

代码语言:javascript
复制
//CommonJS/AMD
const app = require("app")
module.exports = app
exports.app = app

//ES6规范,解构要求标识符对应(但可以用as重命名),引入export default这种的可以自定义变量名,一个文件可以同时export default和export xxx
import app from 'app'// export default xxx
import {login,logout} from 'app'//export const xxx or export function xxx or export {login,logout,...} or export * from 'xxx'
import * from 'xxx'
import {login as logIn} from 'app'
import app ,{login} from 'app'
import * as app from 'app'

#闭包

代码语言:javascript
复制
function outer() {
        let a = 1
        let inner = () => { a++; console.log(a) }
        return inner
      }

闭包利用了函数的执行环境,每次返回的inner都有不同的执行环境,意味着不同的inner分别拥有自己的a值。

PS:上次面头条,竟然没有写出来emm千万不要像我一样。

#constructor 构造函数

#原型链&继承

#Promise

#函数生成器

#async...await

#并发模型与事件循环

JavaScript的并发模型基于事件循环。

先同步,后异步。先执行微任务,后执行宏任务。

Stack, heap, queue
Stack, heap, queue

#Stack 栈

这里的栈指函数调用形成的执行栈。函数具有参数和局部变量,如果函数A调用了函数B,并且执行函数A,那么函数A会被先压入栈,调用B时,函数B被压入栈(位于A之上),到函数B返回,其被弹出。

函数被压入栈的实际过程是压入调用帧。

#Heap 堆

非结构化的存储区域,其中存储对象。

#Queue 队列

JavaScript维护一个待处理的消息队列,而每一个消息与处理它的函数关联。在事件循环中的某个环节,JavaScript按顺序处理Queue的消息。

每当调用处理消息的函数,其形成的调用帧被压入栈。该函数可能会调用其他函数,因此只有当执行栈为空,JavaScript才能继续处理下一个消息。最终,消息队列为空。

#事件循环

代码语言:javascript
复制
while (queue.waitForMessage()){
  queue.processNextMessage();
}

瞧,这就是事件循环,因为它是一个处理消息的循环。其中waitForMessage是同步的,如果没有消息,它就会等。

#不打断地执行

如果你理解了队列的执行方式,那么你会明白这种处理方式意味着函数执行决不会被抢占。(相对于C/C++多线程,你不得不考虑函数被中断的情况)这为编程和分析带来了便利,但代价是消息处理函数可能会长时间阻塞其他事件,如用户的点击、滑动,在这种情况下,浏览器会提示无响应,用户可以选择等待或结束进程。

#不阻塞

MDN声称JavaScript“永不阻塞”,这当然是不对的,例如alert()与同步XHR的场景,但如此声称有它的理由。JavaScript中I/O通常采用事件回调的形式完成,这意味着I/O不会影响其余代码执行。

#添加消息

事件需要绑定监听器以被监听,否则事件将丢失。例如用户点击按钮并被监听到时,消息队列就多了一个消息。

setTimeout(handler, timeOut)允许向队列添加消息,并且设置最小触发延时。延时可能大于设定的时间,因为预定的时间内JavaScript可能正在处理其他消息(即使延时设置为0也一样,并且H5标准规定最小间隔为4ms)。一个简单的例子是,先设定一个定时执行的函数,再令JavaScript进入无限循环,无论何时被设定的函数都不会执行。

#同步代码

JavaScript的同步执行代码可以理解成第一条消息的处理函数,在它执行完前,不会有其他消息被处理。

#Runtime间通信

JavaScript虽然是单线程,但跨域iframe和web worker都是独立的runtime。他们能且只能用postMessage()发送消息,并监听message事件。

#宏任务与微任务

微任务和宏任务指的是setTimeout一样需要被加入队列执行的异步代码,而微任务一定位于宏任务之前。

先祭上这段常见代码:

代码语言:javascript
复制
setTimeout(_ => console.log(4))

new Promise(resolve => {
  resolve()
  console.log(1)
}).then(_ => {
  console.log(3)
})

console.log(2)

Promise是同步代码,Promise.then才是异步代码,所以1,2的顺序是毫无疑问的。3,4都是异步任务,为什么3在4前面呢?如果以事件队列理解,4应该在3前面,但由于3是微任务,4是宏任务,3应该在4之前被处理。

宏任务和微任务都存在于事件循环,但微任务尽管添加时间可能比宏任务晚,仍然要在下一个宏任务执行前执行。事件循环处理消息相当于有两个步骤,第一步检查当前是否有微任务(微任务虽然也是异步代码,但可以看作不在消息队列中,因为它会“插队”),如果有先完成,第二步执行宏任务并在队列中寻找下一个消息。

如果在宏任务执行过程中添加微任务,那么它会在下一个宏任务执行前执行。

代码语言:javascript
复制
setTimeout(_ => {
    Promise.resolve().then(_ => { console.log("Micro") });
    console.log("Macro")
})
//Macro
//Micro

let two = (date) => { while (Date.now() - date < 2000) { } }
let twoWithPromise = (date) => {
    Promise.resolve().then(_ => console.log('Promise'));
    while (Date.now() - date < 2000) { }
}
let fdd = () => {
    let d = Date.now();
    setTimeout(twoWithPromise, 0, d);
    setTimeout(two, 0, d);
    setTimeout(two, 0, d)
}
//2秒后输出Promise,说明twoWithPromise的确花了2s,之后Promise.then执行,再之后才是下一个setTimeout

我在掘金上看到有人说requestAnimationFrame()的触发要先于setTimeout(),他说这是因为修改DOM属性是同步操作,这显然是不对的,同步只是注册监听器。参考评论,理想情况下requestAnimationFrame对于60Hz的显示器来说每16.6ms执行一次,而setTimeout(handler,0)既可能是4ms执行一次,也可能由于页面重新渲染,最小间隔变为16ms。当屏幕刷新率变高,requestAnimationFrame将在setTimeout()之前。

#宏任务与微任务表格

函数/过程

宏任务

微任务

I/O

setTimeout

setInterval

Promise.then/catch/finally

setImmediate(NodeJS)

requestAnimationFrame(Browser)

process.nextTick(NodeJS)

MutationObserver(Browser)

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019年9月8日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • JavaScript进阶
    • #包管理器
      • #NPM
      • #YARN
      • #import与require
    • #闭包
      • #constructor 构造函数
        • #原型链&继承
          • #Promise
            • #函数生成器
            • #async...await
          • #并发模型与事件循环
            • #Stack 栈
            • #Heap 堆
            • #Queue 队列
            • #事件循环
            • #Runtime间通信
            • #宏任务与微任务
        相关产品与服务
        消息队列 CMQ 版
        消息队列 CMQ 版(TDMQ for CMQ,简称 TDMQ CMQ 版)是一款分布式高可用的消息队列服务,它能够提供可靠的,基于消息的异步通信机制,能够将分布式部署的不同应用(或同一应用的不同组件)中的信息传递,存储在可靠有效的 CMQ 队列中,防止消息丢失。TDMQ CMQ 版支持多进程同时读写,收发互不干扰,无需各应用或组件始终处于运行状态。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档