awesome-interview/book3/js-async/index.html

24 lines
51 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="generator" content="Docusaurus v2.0.0-beta.5">
<title data-react-helmet="true">JavaScript 异步编程 | HZFE - 剑指前端 Offer</title><meta data-react-helmet="true" name="twitter:card" content="summary_large_image"><meta data-react-helmet="true" property="og:url" content="https://hzfe.github.io/awesome-interview/book3/js-async"><meta data-react-helmet="true" name="docusaurus_locale" content="en"><meta data-react-helmet="true" name="docusaurus_version" content="current"><meta data-react-helmet="true" name="docusaurus_tag" content="docs-default-current"><meta data-react-helmet="true" property="og:title" content="JavaScript 异步编程 | HZFE - 剑指前端 Offer"><meta data-react-helmet="true" name="description" content="相关问题"><meta data-react-helmet="true" property="og:description" content="相关问题"><link data-react-helmet="true" rel="shortcut icon" href="/awesome-interview/img/favicon.ico"><link data-react-helmet="true" rel="canonical" href="https://hzfe.github.io/awesome-interview/book3/js-async"><link data-react-helmet="true" rel="alternate" href="https://hzfe.github.io/awesome-interview/book3/js-async" hreflang="en"><link data-react-helmet="true" rel="alternate" href="https://hzfe.github.io/awesome-interview/book3/js-async" hreflang="x-default"><link rel="stylesheet" href="/awesome-interview/assets/css/styles.36d1aa8c.css">
<link rel="preload" href="/awesome-interview/assets/js/runtime~main.21038279.js" as="script">
<link rel="preload" href="/awesome-interview/assets/js/main.de89bd10.js" as="script">
</head>
<body>
<script>!function(){function t(t){document.documentElement.setAttribute("data-theme",t)}var e=function(){var t=null;try{t=localStorage.getItem("theme")}catch(t){}return t}();t(null!==e?e:"light")}()</script><div id="__docusaurus">
<div><a href="#" class="skipToContent_1oUP">Skip to main content</a></div><nav class="navbar navbar--fixed-top"><div class="navbar__inner"><div class="navbar__items"><button aria-label="Navigation bar toggle" class="navbar__toggle clean-btn" type="button" tabindex="0"><svg width="30" height="30" viewBox="0 0 30 30" aria-hidden="true"><path stroke="currentColor" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2" d="M4 7h22M4 15h22M4 23h22"></path></svg></button><a class="navbar__brand" href="/awesome-interview/"><img src="/awesome-interview/img/badge.svg" alt="HZFE" class="themedImage_1VuW themedImage--light_3UqQ navbar__logo"><img src="/awesome-interview/img/badge.svg" alt="HZFE" class="themedImage_1VuW themedImage--dark_hz6m navbar__logo"><b class="navbar__title">剑指前端 Offer</b></a></div><div class="navbar__items navbar__items--right"><a href="https://github.com/hzfe/awesome-interview" target="_blank" rel="noopener noreferrer" class="navbar__item navbar__link"><span>GitHub<svg width="13.5" height="13.5" aria-hidden="true" viewBox="0 0 24 24" class="iconExternalLink_3J9K"><path fill="currentColor" d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"></path></svg></span></a></div></div><div role="presentation" class="navbar-sidebar__backdrop"></div></nav><div class="main-wrapper docs-wrapper docs-doc-page"><div class="docPage_31aa"><button class="clean-btn backToTopButton_35hR" type="button"><svg viewBox="0 0 24 24" width="28"><path d="M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z" fill="currentColor"></path></svg></button><aside class="docSidebarContainer_3Kbt"><div class="sidebar_15mo"><nav class="menu thin-scrollbar menu_Bmed menuWithAnnouncementBar_2WvA"><ul class="menu__list"><li class="menu__list-item"><a class="menu__link" href="/awesome-interview/">前言</a></li><li class="menu__list-item menu__list-item--collapsed"><a class="menu__link menu__link--sublist" href="#">模拟题一</a></li><li class="menu__list-item menu__list-item--collapsed"><a class="menu__link menu__link--sublist" href="#">模拟题二</a></li><li class="menu__list-item"><a class="menu__link menu__link--sublist menu__link--active" href="#">模拟题三</a><ul style="display:block;overflow:visible;height:auto" class="menu__list"><li class="menu__list-item"><a class="menu__link" tabindex="0" href="/awesome-interview/book3/browser-event-loop">浏览器:浏览器事件循环</a></li><li class="menu__list-item"><a class="menu__link" tabindex="0" href="/awesome-interview/book3/browser-memory-leaks">浏览器:如何定位内存泄露</a></li><li class="menu__list-item"><a class="menu__link" tabindex="0" href="/awesome-interview/book3/engineer-webpack-loader">工程化:谈下 webpack loader 的机制</a></li><li class="menu__list-item"><a class="menu__link" tabindex="0" href="/awesome-interview/book3/frame-react-hooks">框架React Hooks 实现原理</a></li><li class="menu__list-item"><a class="menu__link" tabindex="0" href="/awesome-interview/book3/frame-diff">框架:常见框架的 Diff 算法</a></li><li class="menu__list-item"><a class="menu__link menu__link--active" aria-current="page" tabindex="0" href="/awesome-interview/book3/js-async">基础JavaScript 异步编程</a></li><li class="menu__list-item"><a class="menu__link" tabindex="0" href="/awesome-interview/book3/js-ts-interface-type">基础TypeScript 中的 Interface 和 Type Alias</a></li><li class="menu__list-item"><a class="menu__link" tabindex="0" href="/awesome-interview/book3/css-mobile-adaptive">样式:移动端自适应的常见手段</a></li><li class="menu__list-item"><a class="menu__link" tabindex="0" href="/awesome-interview/book3/network-http-1-2">网络HTTP2 和 HTTP1.1 的对比</a></li><li class="menu__list-item"><a class="menu__link" tabindex="0" href="/awesome-interview/book3/coding-arr-to-tree">编码:将列表还原为树状结构</a></li><li class="menu__list-item"><a class="menu__link" tabindex="0" href="/awesome-interview/book3/algorithm-binary-tree-k">算法:二叉搜索树的第 k 个结点</a></li><li class="menu__list-item"><a class="menu__link" tabindex="0" href="/awesome-interview/book3/topic-white-screen-optimization">综合:如何减少白屏的时间</a></li></ul></li></ul></nav><button type="button" title="Collapse sidebar" aria-label="Collapse sidebar" class="button button--secondary button--outline collapseSidebarButton_1CGd"><svg width="20" height="20" aria-hidden="true" class="collapseSidebarButtonIcon_3E-R"><g fill="#7a7a7a"><path d="M9.992 10.023c0 .2-.062.399-.172.547l-4.996 7.492a.982.982 0 01-.828.454H1c-.55 0-1-.453-1-1 0-.2.059-.403.168-.551l4.629-6.942L.168 3.078A.939.939 0 010 2.528c0-.548.45-.997 1-.997h2.996c.352 0 .649.18.828.45L9.82 9.472c.11.148.172.347.172.55zm0 0"></path><path d="M19.98 10.023c0 .2-.058.399-.168.547l-4.996 7.492a.987.987 0 01-.828.454h-3c-.547 0-.996-.453-.996-1 0-.2.059-.403.168-.551l4.625-6.942-4.625-6.945a.939.939 0 01-.168-.55 1 1 0 01.996-.997h3c.348 0 .649.18.828.45l4.996 7.492c.11.148.168.347.168.55zm0 0"></path></g></svg></button></div></aside><main class="docMainContainer_3ufF"><div class="container padding-top--md padding-bottom--lg"><div class="row"><div class="col docItemCol_3FnS"><div class="docItemContainer_33ec"><article><div class="tocCollapsible_1PrD tocMobile_3Hoh"><button type="button" class="clean-btn tocCollapsibleButton_2O1e">On this page</button></div><div class="markdown"><header><h1>JavaScript 异步编程</h1></header><h2><a aria-hidden="true" tabindex="-1" class="anchor anchor__h2 anchorWithStickyNavbar_31ik" id="相关问题"></a>相关问题<a class="hash-link" href="#相关问题" title="Direct link to heading">#</a></h2><ul><li>JavaScript 异步编程方案有哪些</li><li>JavaScript 异步编程方案各有什么优缺点</li></ul><h2><a aria-hidden="true" tabindex="-1" class="anchor anchor__h2 anchorWithStickyNavbar_31ik" id="回答关键点"></a>回答关键点<a class="hash-link" href="#回答关键点" title="Direct link to heading">#</a></h2><p><code>阻塞</code> <code>事件循环</code> <code>回调函数</code></p><p>JavaScript 是一种同步的、阻塞的、单线程的语言,一次只能执行一个任务。但浏览器定义了非同步的 Web APIs将回调函数插入到<a href="/awesome-interview/book3/browser-event-loop">事件循环</a>,实现异步任务的非阻塞执行。常见的异步方案有异步回调、定时器、发布/订阅模式、Promise、生成器 Generator、async/await 以及 Web Worker。</p><h2><a aria-hidden="true" tabindex="-1" class="anchor anchor__h2 anchorWithStickyNavbar_31ik" id="知识点深入"></a>知识点深入<a class="hash-link" href="#知识点深入" title="Direct link to heading">#</a></h2><h3><a aria-hidden="true" tabindex="-1" class="anchor anchor__h3 anchorWithStickyNavbar_31ik" id="1-异步回调"></a>1. 异步回调<a class="hash-link" href="#1-异步回调" title="Direct link to heading">#</a></h3><p>异步回调函数作为参数传递给在后台执行的其他函数。当后台运行的代码结束,就调用回调函数,通知工作已经完成。具体示例如下:</p><div class="codeBlockContainer_K1bP"><div class="codeBlockContent_hGly js"><pre tabindex="0" class="prism-code language-js codeBlock_23N8 thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_39YC"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// 第一个参数是监听的事件类型,第二个就是事件发生时调用的回调函数。</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain">btn</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">addEventListener</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">&quot;click&quot;</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token console class-name">console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">log</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">&quot;You clicked me!&quot;</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block">
</span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> pElem </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token dom variable" style="color:#36acaa">document</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">createElement</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">&quot;p&quot;</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> pElem</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">textContent</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">&quot;hello, hzfe.&quot;</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token dom variable" style="color:#36acaa">document</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">body</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">appendChild</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">pElem</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_Ue-o clean-btn">Copy</button></div></div><p>异步回调是编写和处理 JavaScript 异步逻辑的最常用方式,也是最基础的异步模式。但是随着 JavaScript 的发展,异步回调的问题也不容忽视:</p><ol><li>回调表达异步流程的方式是非线性的,非顺序的,理解成本较高。</li><li>回调会受到控制反转的影响。因为回调的控制权在第三方(如 Ajax由第三方来调用回调函数无法确定调用是否符合预期。</li><li>多层嵌套回调会产生回调地狱callback hell</li></ol><h3><a aria-hidden="true" tabindex="-1" class="anchor anchor__h3 anchorWithStickyNavbar_31ik" id="2-定时器settimeoutsetintervalrequestanimationframe"></a>2. 定时器setTimeout/setInterval/requestAnimationFrame<a class="hash-link" href="#2-定时器settimeoutsetintervalrequestanimationframe" title="Direct link to heading">#</a></h3><p>这三个都可以用异步方式运行代码。主要特征如下:</p><ol><li>setTimeout经过任意时间后运行函数<strong>递归 setTimeout 在 JavaScript 线程不阻塞情的况下可保证执行间隔相同</strong></li><li>setInterval允许重复执行一个函数并设置时间间隔<strong>不能保证执行间隔相同</strong></li><li>requestAnimationFrame<strong>当前浏览器/系统的最佳帧速率</strong>重复且高效地运行函数的方法。一般用于处理动画效果。</li></ol><p>setInterval 会按设定的时间间隔固定调用,其中 setInterval 里面的代码的执行时间也包含在内,所以<strong>实际间隔小于设定的时间间隔</strong>。而递归 setTimeout 是调用时才开始算时间,可以保证多次递归调用时的间隔相同。</p><p>如果当前 JavaScript 线程<strong>阻塞</strong>,轮到的 setInterval 无法执行,那么本次任务就会<strong>被丢弃</strong>。而 setTimeout 被阻塞后不会<strong>被丢弃</strong>,等到空闲时会继续执行,但无法保证执行间隔。</p><h3><a aria-hidden="true" tabindex="-1" class="anchor anchor__h3 anchorWithStickyNavbar_31ik" id="3-发布订阅模式publish-subscribe-pattern"></a>3. 发布/订阅模式publish-subscribe pattern<a class="hash-link" href="#3-发布订阅模式publish-subscribe-pattern" title="Direct link to heading">#</a></h3><p><strong>发布/订阅模式是一种对象间一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到状态改变的通知。</strong></p><p>上面异步回调的例子也是一个发布/订阅模式publish-subscribe pattern的实现。订阅 btn 的 click 事件,当 btn 被点击时向订阅者发送这个消息,执行对应的操作。</p><div class="codeBlockContainer_K1bP"><div class="codeBlockContent_hGly js"><pre tabindex="0" class="prism-code language-js codeBlock_23N8 thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_39YC"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">PubSub</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token function" style="color:#d73a49">constructor</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 存储所有订阅的事件类型及对应的订阅函数数组</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// key &lt;eventType&gt;: value &lt;subscribeList&gt;[]</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">handlers</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 订阅事件方法</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token function" style="color:#d73a49">on</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">eventType</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> handler</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">!</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">eventType </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">handlers</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">handlers</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">eventType</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">handlers</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">eventType</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">push</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">handler</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 消息发布方法</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token function" style="color:#d73a49">emit</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">eventType</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> </span><span class="token parameter operator" style="color:#393A34">...</span><span class="token parameter">handlerArgs</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">handlers</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">eventType</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">forEach</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">v</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token function" style="color:#d73a49">v</span><span class="token punctuation" style="color:#393A34">(</span><span class="token spread operator" style="color:#393A34">...</span><span class="token plain">handlerArgs</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 取消订阅</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token function" style="color:#d73a49">remove</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">eventType</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> handler</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 没有传入具体的事件处理函数,则移除该事件类型的所有订阅函数</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 有则在订阅数组中移除对应的函数</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">!</span><span class="token plain">handler</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">handlers</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">eventType</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">length</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">else</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> key </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">handlers</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">eventType</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">findIndex</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">v</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> v </span><span class="token operator" style="color:#393A34">===</span><span class="token plain"> handler</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">key </span><span class="token operator" style="color:#393A34">!==</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">handlers</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">eventType</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">splice</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">key</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block">
</span></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> test1 </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">PubSub</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token function-variable function" style="color:#d73a49">fn1</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter operator" style="color:#393A34">...</span><span class="token parameter">data</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token console class-name">console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">log</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">data</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain">test1</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">on</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">&quot;event1&quot;</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> fn1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain">test1</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">on</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">&quot;event1&quot;</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter operator" style="color:#393A34">...</span><span class="token parameter">data</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token console class-name">console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">log</span><span class="token punctuation" style="color:#393A34">(</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string string" style="color:#e3116c">fn2: </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token template-string interpolation">data</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain">test1</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">emit</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">&quot;event1&quot;</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">&quot;hzfe1&quot;</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">&quot;hzfe2&quot;</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">&quot;hzfe3&quot;</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain">test1</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">remove</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">&quot;event1&quot;</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> fn1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// [&quot;hzfe1&quot;, &quot;hzfe2&quot;, &quot;hzfe3&quot;] fn1打印</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// fn2: hzfe1,hzfe2,hzfe3</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_Ue-o clean-btn">Copy</button></div></div><p>发布/订阅模式可以更细致地了解到有多少种事件类型以及每种类型对应的订阅事件,方便进一步的监听与控制。</p><h3><a aria-hidden="true" tabindex="-1" class="anchor anchor__h3 anchorWithStickyNavbar_31ik" id="4-promise"></a>4. Promise<a class="hash-link" href="#4-promise" title="Direct link to heading">#</a></h3><p>Promise 提供了完成和拒绝两个状态来标识异步操作结果,通过 then 和 catch 可以分别对着两个状态进行跟踪处理。和事件监听的主要差别在于:</p><ol><li>一个 Promise 只能成功或失败一次,一旦状态改变,就无法从成功切换到失败,反之亦然。</li><li>如果 Promise 成功或失败,那么即使在事件发生之后添加成功/失败回调,也将调用正确的回调。</li></ol><p>Promise 使用顺序的方式来表达异步,将回调的控制权转交给了可以信任的 Promise.resolve(),同时也能够使用链式流的方式避免回调地狱的产生,解决了异步回调的问题。但 Promise 也有缺陷:</p><ol><li>顺序错误处理如果不设置回调函数Promise 链中的错误很容易被忽略。</li><li>单决议Promise 只能被决议一次(完成或拒绝),不能很好地支持多次触发的事件及数据流(支持的<a href="https://streams.spec.whatwg.org/" target="_blank" rel="noopener noreferrer">标准</a>正在制定中)。</li><li>无法获取状态:处于 Pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。</li><li>无法取消:一旦创建了 Promise 并注册了完成/拒绝函数,不能取消执行。</li></ol><h3><a aria-hidden="true" tabindex="-1" class="anchor anchor__h3 anchorWithStickyNavbar_31ik" id="5-生成器-generator"></a>5. 生成器 Generator<a class="hash-link" href="#5-生成器-generator" title="Direct link to heading">#</a></h3><p>Generator 函数是 ES6 提供的一种异步编程解决方案,语法与传统函数完全不同,最大的特点就是可以控制函数的执行。简单示例如下:</p><div class="codeBlockContainer_K1bP"><div class="codeBlockContent_hGly js"><pre tabindex="0" class="prism-code language-js codeBlock_23N8 thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_39YC"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">function</span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">helloHzfeGenerator</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">yield</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">&quot;hello&quot;</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">yield</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">&quot;hzfe&quot;</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">&quot;ending&quot;</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block">
</span></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">var</span><span class="token plain"> hello </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">helloHzfeGenerator</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block">
</span></span><span class="token-line" style="color:#393A34"><span class="token plain">hello</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">next</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// { value: &#x27;hello&#x27;, done: false }</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block">
</span></span><span class="token-line" style="color:#393A34"><span class="token plain">hello</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">next</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// { value: &#x27;hzfe&#x27;, done: false }</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block">
</span></span><span class="token-line" style="color:#393A34"><span class="token plain">hello</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">next</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// { value: &#x27;ending&#x27;, done: true }</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block">
</span></span><span class="token-line" style="color:#393A34"><span class="token plain">hello</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">next</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// { value: undefined, done: true }</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_Ue-o clean-btn">Copy</button></div></div><p>生成器 Generator 并不像普通函数那样总是运行到结束,可以在运行当中通过 yield 来暂停并完全保持其状态,再通过 next 恢复运行。yield/next 不只是控制机制也是一种双向消息传递机制。yield 表达式本质上是暂停下来等待某个值next 调用会向被暂停的 yield 表达式传回一个值(或者是隐式的 undefined</p><p>生成器 Generator 保持了顺序、同步、阻塞的代码模式,同样解决了异步回调的问题。</p><h3><a aria-hidden="true" tabindex="-1" class="anchor anchor__h3 anchorWithStickyNavbar_31ik" id="6-aysncawait"></a>6. aysnc/await<a class="hash-link" href="#6-aysncawait" title="Direct link to heading">#</a></h3><p>aysnc/await 属于 <a href="https://262.ecma-international.org/8.0/" target="_blank" rel="noopener noreferrer">ECMAScript 2017 JavaScript 版</a>的一部分,使异步代码更易于编写和阅读。通过使用它们,异步代码看起来更像是同步代码。具有如下特点:</p><ol><li>async/await 不能用于普通的回调函数。</li><li>async/await 与 Promise 一样,是非阻塞的。</li><li>async/await 使得异步代码看起来像同步代码。</li></ol><p>async/await 也存在问题await 关键字会阻塞其后的代码,直到 Promise 完成,就像执行同步操作一样。它可以允许其他任务在此期间继续运行,但自己的代码会被阻塞。解决方案是将 Promise 对象存储在变量中来同时开始,然后等待它们全部执行完毕。具体参照 <a href="https://mdn.github.io/learning-area/javascript/asynchronous/async-await/fast-async-await.html" target="_blank" rel="noopener noreferrer">fast asnyc await</a>。如果内部的 await 等待的异步任务之间没有依赖关系,且需要获取这些异步操作的结果,可以使用 Promise.allSettled() 同时执行这些任务并获得结果。</p><h3><a aria-hidden="true" tabindex="-1" class="anchor anchor__h3 anchorWithStickyNavbar_31ik" id="7-web-worker"></a>7. Web Worker<a class="hash-link" href="#7-web-worker" title="Direct link to heading">#</a></h3><p><strong>Web Worker 为 JavaScript 创造了多线程环境</strong>,允许主线程创建 Worker 线程,将一些任务分配给 Worker 线程运行,处理完后可以通过 postMessage 将结果传递给主线程。优点在于可以在一个单独的线程中执行费时的处理任务,从而允许主线程中的任务(通常是 UI运行不被阻塞/放慢。</p><p>使用 Web Worker 时有以下三点需要注意的地方:</p><ol><li><strong>在 Worker 内部无法访问主线程的任何资源</strong>,包括全局变量,页面的 DOM 或者其他资源,因为这是一个完全独立的线程。</li><li><strong>Worker 和主线程间的数据传递通过消息机制进行</strong>。使用 postMessage 方法发送消息;使用 onmessage 事件处理函数来响应消息。</li><li><strong>Worker 可以创建新的 Worker新的 Worker 和父页面同源</strong>。Worker 在使用 XMLHttpRequest 进行网络 I/O 时XMLHttpRequest 的 responseXML 和 channel 属性会返回 null。</li></ol><p>Web Worker 主要应用场景:</p><ol><li>处理密集型数学计算</li><li>大数据集排序</li><li>数据处理(压缩,音频分析,图像处理等)</li><li>高流量网络通信</li></ol><h2><a aria-hidden="true" tabindex="-1" class="anchor anchor__h2 anchorWithStickyNavbar_31ik" id="参考资料"></a>参考资料<a class="hash-link" href="#参考资料" title="Direct link to heading">#</a></h2><ol><li><a href="https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Asynchronous" target="_blank" rel="noopener noreferrer">异步 JavaScript</a></li><li><a href="https://developer.mozilla.org/zh-CN/docs/Web/API/Web_Workers_API/Using_web_workers" target="_blank" rel="noopener noreferrer">使用 Web Worker</a></li></ol></div></article><nav class="pagination-nav docusaurus-mt-lg" aria-label="Docs pages navigation"><div class="pagination-nav__item"><a class="pagination-nav__link" href="/awesome-interview/book3/frame-diff"><div class="pagination-nav__sublabel">Previous</div><div class="pagination-nav__label">« 框架:常见框架的 Diff 算法</div></a></div><div class="pagination-nav__item pagination-nav__item--next"><a class="pagination-nav__link" href="/awesome-interview/book3/js-ts-interface-type"><div class="pagination-nav__sublabel">Next</div><div class="pagination-nav__label">基础TypeScript 中的 Interface 和 Type Alias »</div></a></div></nav></div></div><div class="col col--3"><div class="tableOfContents_35-E thin-scrollbar"><ul class="table-of-contents table-of-contents__left-border"><li><a href="#相关问题" class="table-of-contents__link">相关问题</a></li><li><a href="#回答关键点" class="table-of-contents__link">回答关键点</a></li><li><a href="#知识点深入" class="table-of-contents__link">知识点深入</a><ul><li><a href="#1-异步回调" class="table-of-contents__link">1. 异步回调</a></li><li><a href="#2-定时器settimeoutsetintervalrequestanimationframe" class="table-of-contents__link">2. 定时器setTimeout/setInterval/requestAnimationFrame</a></li><li><a href="#3-发布订阅模式publish-subscribe-pattern" class="table-of-contents__link">3. 发布/订阅模式publish-subscribe pattern</a></li><li><a href="#4-promise" class="table-of-contents__link">4. Promise</a></li><li><a href="#5-生成器-generator" class="table-of-contents__link">5. 生成器 Generator</a></li><li><a href="#6-aysncawait" class="table-of-contents__link">6. aysnc/await</a></li><li><a href="#7-web-worker" class="table-of-contents__link">7. Web Worker</a></li></ul></li><li><a href="#参考资料" class="table-of-contents__link">参考资料</a></li></ul></div></div></div><div class="row"><div class="col"><div class="react-utterences"><div>Loading script...</div></div></div><div class="col col--3"></div></div></div></main></div></div></div>
<script src="/awesome-interview/assets/js/runtime~main.21038279.js"></script>
<script src="/awesome-interview/assets/js/main.de89bd10.js"></script>
</body>
</html>