diff options
author | MDN <actions@users.noreply.github.com> | 2022-03-08 00:12:08 +0000 |
---|---|---|
committer | MDN <actions@users.noreply.github.com> | 2022-03-08 00:12:08 +0000 |
commit | d53bae971e455859229bcb3246e29bcbc85179d2 (patch) | |
tree | 396c8a403d46802b365aa8d26183f6b18f2a1c6e /files/zh-cn/learn/javascript/asynchronous/concepts/index.html | |
parent | 0c6e68897d9b4182437d58e376dbb48ee6947f71 (diff) | |
download | translated-content-d53bae971e455859229bcb3246e29bcbc85179d2.tar.gz translated-content-d53bae971e455859229bcb3246e29bcbc85179d2.tar.bz2 translated-content-d53bae971e455859229bcb3246e29bcbc85179d2.zip |
[CRON] sync translated content
Diffstat (limited to 'files/zh-cn/learn/javascript/asynchronous/concepts/index.html')
-rw-r--r-- | files/zh-cn/learn/javascript/asynchronous/concepts/index.html | 163 |
1 files changed, 0 insertions, 163 deletions
diff --git a/files/zh-cn/learn/javascript/asynchronous/concepts/index.html b/files/zh-cn/learn/javascript/asynchronous/concepts/index.html deleted file mode 100644 index 757eee777c..0000000000 --- a/files/zh-cn/learn/javascript/asynchronous/concepts/index.html +++ /dev/null @@ -1,163 +0,0 @@ ---- -title: 通用异步编程概念 -slug: Learn/JavaScript/Asynchronous/Concepts -tags: - - JavaScript - - Promises - - Threads - - 学习 - - 异步 - - 阻塞 -translation_of: Learn/JavaScript/Asynchronous/Concepts -original_slug: learn/JavaScript/异步/概念 ---- -<div>{{LearnSidebar}}{{NextMenu("Learn/JavaScript/Asynchronous/Introducing", "Learn/JavaScript/Asynchronous")}}</div> - -<p>在本文中,我们将介绍与异步编程相关的一些重要概念,以及它们在浏览器和JavaScript里的体现。在学习本系列的其他文章之前,你应该先理解这些概念。</p> - -<table class="learn-box standard-table"> - <tbody> - <tr> - <th scope="row">预备条件:</th> - <td>拥有基本的计算机知识,对JavaScript原理有一定了解。</td> - </tr> - <tr> - <th scope="row">目标:</th> - <td>理解异步编程的基本概念,以及异步编程在浏览器和JavaScript里面的表现。</td> - </tr> - </tbody> -</table> - -<h2 id="异步">异步?</h2> - -<p>通常来说,程序都是顺序执行,同一时刻只会发生一件事。如果一个函数依赖于另一个函数的结果,它只能等待那个函数结束才能继续执行,从用户的角度来说,整个程序才算运行完毕.</p> - -<p>Mac 用户有时会经历过这种旋转的彩虹光标(常称为沙滩球),操作系统通过这个光标告诉用户:“现在运行的程序正在等待其他的某一件事情完成,才能继续运行,都这么长的时间了,你一定在担心到底发生了什么事情”。</p> - -<p><img alt="multi-colored macos beachball busy spinner" src="https://mdn.mozillademos.org/files/16577/beachball.jpg" style="display: block; margin: 0 auto;"></p> - -<p>这是令人沮丧的体验,没有充分利用计算机的计算能力 — 尤其是在计算机普遍都有多核CPU的时代,坐在那里等待毫无意义,你完全可以在另一个处理器内核上干其他的工作,同时计算机完成耗时任务的时候通知你。这样你可以同时完成其他工作,这就是<strong>异步编程</strong>的出发点。你正在使用的编程环境(就web开发而言,编程环境就是web浏览器)负责为你提供异步运行此类任务的API。</p> - -<h2 id="产生阻塞的代码">产生阻塞的代码</h2> - -<p>异步技术非常有用,特别是在web编程。当浏览器里面的一个web应用进行密集运算还没有把控制权返回给浏览器的时候,整个浏览器就像冻僵了一样,这叫做<strong>阻塞;</strong>这时候浏览器无法继续处理用户的输入并执行其他任务,直到web应用交回处理器的控制。</p> - -<p>我们来看一些<strong>阻塞</strong>的例子。</p> - -<p>例子: <a href="https://github.com/mdn/learning-area/tree/master/javascript/asynchronous/introducing">simple-sync.html</a> (<a href="https://mdn.github.io/learning-area/javascript/asynchronous/introducing/simple-sync.html">see it running live</a>), 在按钮上添加了一个事件监听器,当按钮被点击,它就开始运行一个非常耗时的任务(计算1千万个日期,并在console里显示最后一个日期),然后在DOM里面添加一个段落:</p> - -<pre class="brush: js">const btn = document.querySelector('button'); -btn.addEventListener('click', () => { - let myDate; - for(let i = 0; i < 10000000; i++) { - let date = new Date(); - myDate = date - } - - console.log(myDate); - - let pElem = document.createElement('p'); - pElem.textContent = 'This is a newly-added paragraph.'; - document.body.appendChild(pElem); -});</pre> - -<p>运行这个例子的时候,打开JavaScript console,然后点击按钮 — 你会注意到,直到日期的运算结束,最后一个日期在console上显示出来,段落才会出现在网页上。代码按照源代码的顺序执行,只有前面的代码结束运行,后面的代码才会执行。</p> - -<div class="blockIndicator note"> -<p><strong>Note</strong>: 这个例子不现实:在实际情况中一般不会发生,没有谁会计算1千万次日期,它仅仅提供一个非常直观的体验.</p> -</div> - -<p>第二个例子, <a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/introducing/simple-sync-ui-blocking.html">simple-sync-ui-blocking.html</a> (<a href="https://mdn.github.io/learning-area/javascript/asynchronous/introducing/simple-sync-ui-blocking.html">see it live</a>), 我们模拟一个在现实的网页可能遇到的情况:因为渲染UI而阻塞用户的互动,这个例子有2个按钮:</p> - -<ul> - <li>"Fill canvas" : 点击的时候用1百万个蓝色的圆填满整个{{htmlelement("canvas")}} .</li> - <li>"Click me for alert" :点击显示alert 消息.</li> -</ul> - -<pre class="brush: js">function expensiveOperation() { - for(let i = 0; i < 1000000; i++) { - ctx.fillStyle = 'rgba(0,0,255, 0.2)'; - ctx.beginPath(); - ctx.arc(random(0, canvas.width), random(0, canvas.height), 10, degToRad(0), degToRad(360), false); - ctx.fill() - } -} - -fillBtn.addEventListener('click', expensiveOperation); - -alertBtn.addEventListener('click', () => - alert('You clicked me!') -);</pre> - -<p>如果你点击第一个按钮,然后快速点击第二个,会注意到alert消息并没有出现,只有等到圆圈都画完以后,才会出现:因为第一个操作没有完成之前阻塞了第二个操作的运行.</p> - -<div class="blockIndicator note"> -<p><strong>Note</strong>: 当然,这个例子也很丑陋,因为我们只是在模拟阻塞效果。但在现实中,这是一个很常见的问题。开发人员们一直在努力缓解它。</p> -</div> - -<p>为什么是这样? 答案是:JavaScript一般来说是单线程的(<strong>single threaded</strong>)<strong>。</strong>接着我们来介绍<strong>线程</strong>的概念。</p> - -<h2 id="线程">线程</h2> - -<p>一个<strong>线程</strong>是一个基本的处理过程,程序用它来完成任务。每个线程一次只能执行一个任务:</p> - -<pre>Task A --> Task B --> Task C</pre> - -<p>每个任务顺序执行,只有前面的结束了,后面的才能开始。</p> - -<p>正如我们之前所说,现在的计算机大都有多个内核(core),因此可以同时执行多个任务。支持多线程的编程语言可以使用计算机的多个内核,同时完成多个任务:</p> - -<pre>Thread 1: Task A --> Task B -Thread 2: Task C --> Task D</pre> - -<h3 id="JavaScript_是单线程的">JavaScript 是单线程的</h3> - -<p>JavaScript 传统上是单线程的。即使有多个内核,也只能在单一线程上运行多个任务,此线程称为主线程(<strong>main thread</strong>)。我们上面的例子运行如下:</p> - -<pre>Main thread: Render circles to canvas --> Display alert()</pre> - -<p>经过一段时间,JavaScript获得了一些工具来帮助解决这种问题。通过 <a href="/en-US/docs/Web/API/Web_Workers_API">Web workers</a> 可以把一些任务交给一个名为worker的单独的线程,这样就可以同时运行多个JavaScript代码块。一般来说,用一个worker来运行一个耗时的任务,主线程就可以处理用户的交互(避免了阻塞)</p> - -<pre>Main thread: Task A --> Task C -Worker thread: Expensive task B</pre> - -<p>记住这些,请查看<a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/introducing/simple-sync-worker.html">simple-sync-worker.html</a> (<a href="https://mdn.github.io/learning-area/javascript/asynchronous/introducing/simple-sync-worker.html">see it running live</a>) , 再次打开浏览器的JavaScript 控制台。这个例子重写了前例:在一个单独的worker线程中计算一千万次日期,你再点击按钮,现在浏览器可以在日期计算完成之前显示段落,阻塞消失了。</p> - -<h2 id="异步代码">异步代码</h2> - -<p>web workers相当有用,但是他们确实也有局限。主要的一个问题是他们不能访问 {{Glossary("DOM")}} — 不能让一个worker直接更新UI。我们不能在worker里面渲染1百万个蓝色圆圈,它基本上只能做算数的苦活。</p> - -<p>其次,虽然在worker里面运行的代码不会产生阻塞,但是基本上还是同步的。当一个函数依赖于几个在它之前运行的过程的结果,这就会成为问题。考虑下面的情况:</p> - -<pre>Main thread: Task A --> Task B</pre> - -<p>在这种情况下,比如说Task A 正在从服务器上获取一个图片之类的资源,Task B 准备在图片上加一个滤镜。如果开始运行Task A 后立即尝试运行Task B,你将会得到一个错误,因为图像还没有获取到。</p> - -<pre>Main thread: Task A --> Task B --> |Task D| -Worker thread: Task C -----------> | |</pre> - -<p>在这种情况下,假设Task D 要同时使用 Task B 和Task C的结果,如果我们能保证这两个结果同时提供,程序可能正常运行,但是这不太可能。如果Task D 尝试在其中一个结果尚未可用的情况下就运行,程序就会抛出一个错误。</p> - -<p>为了解决这些问题,浏览器允许我们异步运行某些操作。像<a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise">Promises</a> 这样的功能就允许让一些操作运行 (比如:从服务器上获取图片),然后等待直到结果返回,再运行其他的操作:</p> - -<pre>Main thread: Task A Task B - Promise: |__async operation__|</pre> - -<p>由于操作发生在其他地方,因此在处理异步操作的时候,主线程不会被阻塞。</p> - -<p><span class="tlid-translation translation" lang="zh-CN"><span title="">我们将在下一篇文章中开始研究如何编写异步代码。</span> <span title="">非常令人兴奋,对吧?</span> <span title="">继续阅读!</span></span></p> - -<h2 id="总结">总结</h2> - -<p>围绕异步编程领域,现代软件设计正在加速旋转,就为了让程序在一个时间内做更多的事情。当你使用更新更强大的API时,你会发现在更多的情况下,使用异步编程是唯一的途径。以前写异步代码很困难,现在也需要你来适应,但是已经变容易了很多。在余下的部分,我们将进一步探讨异步代码的重要性,以及如何设计代码来防止前面已经提到过的问题。</p> - -<h2 id="模块大纲">模块大纲</h2> - -<ul> - <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Concepts">通用异步编程概念</a></li> - <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Introducing">介绍异步JavaScript</a></li> - <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Timeouts_and_intervals">合作异步JavaScript: Timeouts and intervals</a></li> - <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Promises">使用Promises优雅的异步编程</a></li> - <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Async_await">async and await:让异步编程更容易</a></li> - <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Choosing_the_right_approach">选择正确的方法</a></li> -</ul> |