js浏览器下并发模型和事件循环

  • js是单线程语言,意味着同一时间只能处理一个函数。所以每一个消息任务被完整执行完之后,才会执行下一个消息。

执行到完这个模型的一个缺点在于当一个消息需要太长时间才能处理完毕时,Web应用就无法处理用户的交互,例如点击或滚动。浏览器用“程序需要过长时间运行”的对话框来缓解这个问题。

  • 从概念来看,根据任务的执行时间的长短,js中把各种任务分成两大类:同步和异步。
    同步任务:在它没有完成之前,无法执行其他任务。
    异步任务:在执行过程中,则会被先挂起,先执行其他任务,等到程序空闲了,再来执行异步任务的剩余内容。
  • 单线程的JS是通过多任务队列和事件循环(event loop)机制异步任务不阻塞其他任务。

任务:js中一些较为费时的操作被定义为任务

  • 微任务(microtasks):process.nextTick、promise.then、Object.observe、MutationObserver
  • 宏任务(macrotasks,也称作task):setTimeout、setInterval、setImmediate、I/O、UI渲染、script标签中的整体代码、postMessage、MessageChannel
    微任务,宏任务

任务队列:存放task集合的数据结构

  • 任务队列(task queue or macrotask queue ):其实是Sets结构(列表中的每个元素都不可重复),因为事件循环模型的第一步任务是抓取第一个可运行的task而不是直接出队第一个任务。
    宏任务可以有多个队列。

  • 微任务队列(microtask queue)不属于任务队列。
    微任务只有一个队列。

显然,微任务队列中均为微任务,宏任务同理。

JS执行栈

  • 可以简单理解为js代码用来执行的数据结构

当我们调用一个方法的时候,js会生成一个与这个方法对应的执行环境(context),又叫执行上下文。
这个执行环境中存在着这个方法的私有作用域,上层作用域的指向,方法的参数,这个作用域中定义的变量以及这个作用域的this对象。
而当一系列方法被依次调用的时候,因为js是单线程的,同一时间只能执行一个方法,于是这些方法被排队在一个单独的地方。这个地方被称为执行栈。
当一个脚本第一次执行的时候,js引擎会解析这段代码,并将其中的同步代码按照执行顺序加入执行栈中,然后从头开始执行。
如果当前执行的是一个方法Function,那么js会向执行栈中添加这个方法的执行环境,然后进入这个执行环境继续执行其中的代码。
当这个执行环境中的代码 执行完毕并返回结果后,js会退出这个执行环境并把这个执行环境销毁,回到上一个方法的执行环境。
这个过程反复进行,直到执行栈中的代码全部执行完毕。
如果在执行过程中遇到异步代码,会先将其挂起,继续处理同步代码。而异步事件回调的处理,则是依靠事件循环和任务列队机制来调度。
JS执行栈

JS主线程

主线程是用来执行代码的,会不断的去取执行栈中的代码执行,执行完毕之后,代码从主线程中出栈。

事件循环(event loop)具体过程

下列代码中,Promise.then和setTimeout为异步操作,其他代码执行则是同步的。

console.log('script start');

setTimeout(function() {
  console.log('setTimeout');
}, 1000);

Promise.resolve().then(function() {
  console.log('promise1');
}).then(function() {
  console.log('promise2');
});

console.log('script end');
function test(){
  console.log('test');
}
console.log('script test');
test();

setTimeout(function() {
  console.log('setTimeout last');
}, 0);

代码参考:tasks-microtasks-queues-and-schedules/
输出结果:

script start
script end
script test
test
promise1
promise2
setTimeout last
setTimeout
  • 当JS代码执行时,会创建一个执行栈、一个或者多个任务队列、一个微任务队列。

以上述代码为例子:

1.执行栈初始化,console.log压入栈中,主线程执行,打印script start ,然后出栈。
2.setTimeout压入执行栈中,主线程调用时,发现是异步操作,且为宏任务,等待时间是1s,此时回调还没有返回,因此把setTimeout先挂起。
3.Promise.resolve在执行栈中执行,但是其是微任务,Promise.then 1回调进入微任务队列中。
4.console.log压入执行栈,直接执行完毕,打印script end
5.继续读取代码,后续console.log('script test')test();都是同步代码,在执行栈中执行结束,打印了script testtest
6.第二个setTimeout压入执行栈,主线程执行,其回调是异步事件,且为宏任务,等待时间是0,setTimeout被挂起,然后其回调立刻进入宏任务队列。
7.执行栈已经空闲,接着查看微任务队列是否有任务,取出了Promise.then 1任务到执行栈中执行。打印了promise1,then1返回了undefined,执行到了then2,then2回调触发,进入微任务队列当中。
8.then1执行完后出对队了,执行栈也空出来了,此时微任务队列当中还有then2的回调没有执行,then2的回调开始执行,打印了promise 2,执行完之后出队,此时微任务队列被清空了。
9.在2步之后,如果时间超过1s,第一个setTimeout回调已经返回,进入宏任务队列,此时队列中,按照先后顺序,分别是console.log(setTimeout last)回调和console.log(setTimeout)回调。
10.微任务队列空了之后,查看宏任务队列,队列中还有setTimeout的回调没有处理,所以处理这个回调,打印setTimeout

  • 总的来说,就是js中遇到同步代码的时候,会直接在执行栈当中执行。遇到异步代码的时候,异步代码的调用是同步的,但是其回调是异步的,异步回调会等到返回结果之后,根据类型,放入微任务队列或者宏任务队列当中。

  • 当执行栈为空的时候,先处理微任务队列中的任务,清空微任务队列之后,再执行宏任务队列的任务。

event loop 介绍
mozilla Memory_Management
event-loops
《Help, I'm stuck in an event-loop》
ruanyifeng-event-loop

内容关联问题

1.addEventListener多次绑定和onclick多次绑定执行结果差别和原因?
2.setTimeout任务执行时间为什么跟实际执行时间有差异

  • setTimeout的指定的任务等待时间,一般实际等待时间>=指定的等待时间。因为setTimeout的回调会进入宏任务队列去等待。它必须要等执行栈空,且微任务队列空才能执行,因此实际执行时间往往会比指定的要晚。

3.介绍Promise等异步任务的执行顺序

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,029评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,238评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,576评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,214评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,324评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,392评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,416评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,196评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,631评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,919评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,090评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,767评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,410评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,090评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,328评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,952评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,979评论 2 351

推荐阅读更多精彩内容