11、手把手教 Vue--彻底搞懂 JS 异步编程

本节大纲

PS:转载请注明出处
作者: TigerChain
地址: https://www.jianshu.com/p/876e68fd6a1c
本文出自 TigerChain 简书 手把手教 Vue 系列

教程简介

  • 1、阅读对象
    本篇教程适合新手阅读,老手直接略过
  • 2、教程难度
    初级,本人水平有限,文章内容难免会出现问题,如果有问题欢迎指出,谢谢

正文

不知你有没有听说过 js 是一门单线程、非阻塞、异步、并发语言「或者你常常给别人也这样说」。不晓得大伙听到这句话的时候有没有什么疑问,笔者当初听到这句话的时候---W T F 矛盾,天大的矛盾,主要疑惑有三点

1、单线程怎么可能异步「一个线程你玩个毛的异步」?
2、单线程是顺序执行的,非阻塞,一定会阻塞开国际玩笑呢?
3、单线程还并发,想不通,实在想不通?

哪怕你只是听说 js 是单线程的,但是能想到以上 3 点之一,都说明你是一个爱思考的童鞋

首先确定一下,上面那句话是正确无疑的,那话是正确的,既然话没有问题,那有问题的肯定是我们了,我们理解的还不够深刻,接下来我们就一步步揭开 js 的神秘面纱吧,首先我们看看同步和异步的区别

一、同步和异步

同步

同步指的是任务是一个接一个的去完成,上一个任务没有完成,下一个任务就不能开始,单线程和多线程都可以实现同步,但是单线程一定是同步的「一个线程只有执行完前面的任务,才能执行后面的」

sync

异步

异步是一个相对概念,多线程是异步的前提,一个线程是玩不了异步的

async

比如 Java 语言,声明一个 Thread 看起来只有一个线程,但是调用 start() 方法却异步执行了,请看图

java-async-thread

执行结果

show-java-thread-result.png

从结果来看 java 默认是有一个主线程的「main 线程,上面的 thrad 异步就是相对于 main 而言的」,所以根本不可能一个线程就能完成异步

那么到底 js 是如何实现异步的呢?说异步我们不得不说以下几个角色
JavaScript Engine、Web APIs、Message queue、Event loop,接下来一一介绍,首先登场的是 JS 引擎

二、JavaScript Engine

JS 引擎有的也称为 JS 虚拟机,主要是负责解析和执行 js 的,它是浏览器所实现的,不同的浏览器有不同的实现方式「采用 c/c++ 实现」,这里以 V8 引擎为例来说明「其它的引擎都大同小异」

来看看引擎的简易图

js-engine

由图可知,JS 引擎主要包括两个组件就是堆和栈

堆: 用就是用来分配内存的地方

栈: 也叫 调用栈/执行栈 就是方法调用和执行的地方「js 是单线程说的就是 call stack 」

这里顺便说一下,浏览器有渲染引擎和 js 引擎,浏览器是从上向下解析 html 标签的,当遇到 script 标签「js 代码」时会立即停止解析,直接执行 js 脚本,所以渲染引擎和 js 引擎是互斥的「js 操作 DOM 的会影响渲染」,这一个过程是同步的,所以加载一个耗时的 js 会导致界面卡死的根本原因就在这里,有兴趣的可以看看浏览器的渲染引擎这方面内容,或者后面我专写一篇此类文章「扯的远了,脉动回来」

来看看 stack 的执行机制

先来一段代码

call-statck-js-demo

这段代码本身没有什么好说的,非常简单的代码,我们看看 js 引擎在执行这段代码的时候 call stack 中的执行过程

call-stack-exec-procress

call stack 由名子可以看出它是一个栈结构「那肯定遵循先进后出原则」,当一个方法调用的时候就入栈,执行完成以后就出栈

再来一段暴力代码

loop-call-js

以上代码是一段暴力代码,就是一个死循环,我们来看看结果

call-statck-out

由结果可行,call stack 栈大小被撑爆了,其实可以想像,不停的调用 hello 方法,入栈、入栈 ... 入栈,肯定最后就放不下了

栈有多大?

由上面的死循环代码我们就可以尝试着算出 call stack 的大小参考 2ality.com

cal-call-stack-size

当然对不同的浏览器结果是不一样的,引擎实现的方式不一样,我测试在 chorome 如下「不同浏览器大家可自行测试一下」

show-cal-call-stack-size-result

我们清楚了当调用一个方法的时候 js 引擎会把方法压入 call stack ,当方法执行完毕以后出栈

三、Web APIs

由于 js 引擎中的 stack 同一时间只能干一件事情「单线程」,那么 call stack 肯定是玩不了异步,可是虽然 js 是单线程的,但浏览器却是多线程的,我们知道 js 有好多 API 有些不是核心 js 语言的一部分,比如 BOM DOM AJAX setTimeOut Canvas WegGl 等 api 浏览器可以在调用之外执行这些 api 「另起一个或多个线程跑这些 api」

web-api

这些 api 就可以独立于调用栈来执行自己的功能,但是有一个问题是如果这些 api 执行完以后该怎么办呢?有两种方案

  • 1、我们将 web api 完成的方法直接推送到调用栈
  • 2、我们采取一些机制来保存这些响应,在合适的时候推送给调用栈

第 1 种方法显然不靠谱,如果 web api 执行完以后直接把结果给调用栈可以会影响正在执行的调用栈,所以浏览器采用第二种方法,使用消息队列来保存这些 web api 执行的响应以便在调用栈可以调用的时候推送给调用栈,这个保存消息的东西就是接下来我们要说的 Message Queue

四、Message Queue

Message Queue「消息队列也叫 Callback Queue」是用来保存 Web Api 调用完成以后的所有消息的回调函数,当调用栈「call stack」为空时「也就是调用栈中的方法执行完毕以后」Message Queue 中的回调方法「先进先出」会被添加到调用栈中去执行,但是浏览器是什么方式来把调用栈和 message Queue 联系起来的「什么机制把 Message Queue 中的回调方法给 call stack 当 call statck 为空的时候」,它就是 Event Loop

message-queue

五、Event Loop

Event Loop 是把 call stack 和 Message Queue 联系起来的纽带和桥梁,Event Loop 是一个基于事件的并发模型,它时刻在监听着消息队列,如果有完成的消息它此刻还要关心 call stack 是否为空,如果为空则把 Messag Queue 中的回调结果推送给 call statck 回调方法执行

Event Loop 做两件事情

  • 1、监听 Message Queue「是否有消息」
  • 2、监听 call statck 「看是否为空,如果为空则推送结果」
event-loop.jpg

这样就完成了 js 的非阻塞异步调用

六、代码来分析异步调用过程

写一段如下代码

async-run-procress

非常简单的一段代码,体现了 js 的异步过程

分析过程

js-async-fx.jpg

上图显示了上述代码的执行过程,简单的说一下吧,上述代码分为十个步骤

  • 1、当代码执行 console.log('大家好!')的时候此方法入栈,这个没有什么好说的,前面说过了
  • 2、方法继续向下执行遇到 setTimeout()方法,这是一个 webapi 方法然后交给 3 去执行
  • 3、浏览器单独开一个线程去执行,然后调用栈不停止继续向下执行
  • 4、调用栈执行 console.log('欢迎关注') 方法
  • 5、web api 执行 setTimeout 方法完毕,指导结果给 Message Queue ,此是 webpai 就变成空的「图上没有体现,希望明白」
  • 6、此时调用栈中执行完 console.log('欢迎关注') 以后此方法出栈,栈此时变成空的,Event Loop 监听着 Message Queue
  • 7、Event loop 把 Message Queue 中的方法取出来,推给空的调用栈,此时 callback 入栈「调用栈」
  • 8、执行其中的方法体 console.log('TigerChain')
  • 9、打印了 TigerChain 此方法出栈
  • 10、到此 callback 执行完毕,callback 出栈,调用栈变为空

以上过程只是一个针对简单代码的一个简单的分析,如果存在多个异步操作,则 Event Loop 不停的执行取出消息推入栈的操作直到完成

七、总结

到此我们把 js 非阻塞和异步的原理大概说了一下,相信大家应该有一个简单的知识和了解,大概总结一下

  • 1、js 是非阻塞异步的单线程「单线程指的就是 call stack」
  • 2、js 实现异步的方式是基于 Event Loop 的并发模型
  • 3、浏览器的 web api 不是 js 核心的部分,但是和 call stack 不冲突执行「浏览器另外开线程去执行」
  • 4、web api 的执行结果不能直接给 call stack 先要通过 Message Queu 把结果存起来,等待 Event Loop 去处理
  • 5、Event Loop 如果发现 call statck 为空时「此时就是推入 Message Queue 中的消息的最佳时机」取出消息队列中的消息推入给调用栈,异步结束

我们来看一张非常形象的 Event Loop 并发模型的图

event-loop-pic

图片来源 The Main Event… Loop 建议把这张图印在心里、印在心、印在心里「重要的事情说三遍」

js 的异步执行过程从上图非常形象的展现出来,从 statck 开始顺时针执行,遇到 webpai 方法让其去调用并把结果给 Message Queue , Event Loop 查看 stack 为空则取出 Message Queue 中的方法给 statck 搞懂此图就彻底了解了 Event Loop 的并发模型了

本节就到此结束了,源码就不给了「非常简单的例子」,自行照着敲一下比什么都好

八、参考资料

更多文章关注:手把手教Vue系列


点赞富一生「你一点赞我就更来劲了」,转发富五代,更多文章请关注我的微信公号来查阅

公众号:TigerChain

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

推荐阅读更多精彩内容