diff options
Diffstat (limited to 'files/zh-cn/web/api/web_workers_api')
3 files changed, 1267 insertions, 0 deletions
diff --git a/files/zh-cn/web/api/web_workers_api/functions_and_classes_available_to_workers/index.html b/files/zh-cn/web/api/web_workers_api/functions_and_classes_available_to_workers/index.html new file mode 100644 index 0000000000..f859a3d938 --- /dev/null +++ b/files/zh-cn/web/api/web_workers_api/functions_and_classes_available_to_workers/index.html @@ -0,0 +1,371 @@ +--- +title: Web Workers可以使用的函数和类 +slug: Web/API/Web_Workers_API/Functions_and_classes_available_to_workers +tags: + - functions and classes available to Web Workers +translation_of: Web/API/Web_Workers_API/Functions_and_classes_available_to_workers +--- +<p>除了标准的 <a href="/en-US/docs/Web/JavaScript">JavaScript</a> 函数集 (例如 {{jsxref("String")}}, {{jsxref("Array")}}, {{jsxref("Object")}}, {{jsxref("JSON")}} 等), DOM有多种功能可供 workers使用。</p> + +<p>本文提供了一些列表。</p> + +<p><strong>Workers </strong>在另一个全局上下文内运行<strong>, {{domxref("DedicatedWorkerGlobalScope")}} </strong>与当前<strong> window</strong>不同。</p> + +<p>默认情况下,{{domxref("Window")}}的方法和属性不可用,但{{domxref("DedicatedWorkerGlobalScope")}}像Window,实现{{domxref("WindowTimers")}} 和 {{domxref("WindowBase64")}}。</p> + + + +<h2 id="比较不同类型workers的属性和方法">比较不同类型workers的属性和方法</h2> + +<table class="standard-table"> + <tbody> + <tr> + <td class="header">Function</td> + <td class="header">Dedicated workers</td> + <td class="header">Shared workers</td> + <td class="header">Service workers</td> + <td class="header">Chrome workers {{Non-standard_inline}}</td> + <td class="header">Outside workers</td> + </tr> + <tr> + <td>{{domxref("WindowBase64.atob", "atob()")}}</td> + <td>yes, on {{domxref("WorkerGlobalScope")}}</td> + <td>yes, on {{domxref("WorkerGlobalScope")}}</td> + <td>yes, on {{domxref("WorkerGlobalScope")}}</td> + <td>yes, on {{domxref("WorkerGlobalScope")}}</td> + <td>yes, on {{domxref("Window")}}</td> + </tr> + <tr> + <td>{{domxref("WindowBase64.btoa", "btoa()")}}</td> + <td>yes, on {{domxref("WorkerGlobalScope")}}</td> + <td>yes, on {{domxref("WorkerGlobalScope")}}</td> + <td>yes, on {{domxref("WorkerGlobalScope")}}</td> + <td>yes, on {{domxref("WorkerGlobalScope")}}</td> + <td>yes, on {{domxref("Window")}}</td> + </tr> + <tr> + <td>{{domxref("WindowTimers.clearInterval", "clearInterval()")}}</td> + <td>yes, on {{domxref("WorkerGlobalScope")}}</td> + <td>yes, on {{domxref("WorkerGlobalScope")}}</td> + <td>yes, on {{domxref("WorkerGlobalScope")}}</td> + <td>yes, on {{domxref("WorkerGlobalScope")}}</td> + <td>yes, on {{domxref("Window")}}</td> + </tr> + <tr> + <td>{{domxref("WindowTimers.clearTimeout", "clearTimeout()")}}</td> + <td>yes, on {{domxref("WorkerGlobalScope")}}</td> + <td>yes, on {{domxref("WorkerGlobalScope")}}</td> + <td>yes, on {{domxref("WorkerGlobalScope")}}</td> + <td>yes, on {{domxref("WorkerGlobalScope")}}</td> + <td>yes, on {{domxref("Window")}}</td> + </tr> + <tr> + <td>{{domxref("Window.dump()", "dump()")}} {{non-standard_inline}}</td> + <td>yes, on {{domxref("WorkerGlobalScope")}}</td> + <td>yes, on {{domxref("WorkerGlobalScope")}}</td> + <td>yes, on {{domxref("WorkerGlobalScope")}}</td> + <td>yes, on {{domxref("WorkerGlobalScope")}}</td> + <td>yes, on {{domxref("Window")}}</td> + </tr> + <tr> + <td>{{domxref("WindowTimers.setInterval", "setInterval()")}}</td> + <td>yes, on {{domxref("WorkerGlobalScope")}}</td> + <td>yes, on {{domxref("WorkerGlobalScope")}}</td> + <td>yes, on {{domxref("WorkerGlobalScope")}}</td> + <td>yes, on {{domxref("WorkerGlobalScope")}}</td> + <td>yes, on {{domxref("Window")}}</td> + </tr> + <tr> + <td>{{domxref("WindowTimers.setTimeout", "setTimeout()")}}</td> + <td>yes, on {{domxref("WorkerGlobalScope")}}</td> + <td>yes, on {{domxref("WorkerGlobalScope")}}</td> + <td>yes, on {{domxref("WorkerGlobalScope")}}</td> + <td>yes, on {{domxref("WorkerGlobalScope")}}</td> + <td>yes, on {{domxref("Window")}}</td> + </tr> + <tr> + <td>{{domxref("WorkerGlobalScope.importScripts", "importScripts()")}}</td> + <td>yes, on {{domxref("WorkerGlobalScope")}}</td> + <td>yes, on {{domxref("WorkerGlobalScope")}}</td> + <td>yes, on {{domxref("WorkerGlobalScope")}}</td> + <td>yes, on {{domxref("WorkerGlobalScope")}}</td> + <td>no</td> + </tr> + <tr> + <td>{{domxref("WorkerGlobalScope.close", "close()")}} {{non-standard_inline}}</td> + <td>yes, on {{domxref("WorkerGlobalScope")}}</td> + <td>yes, on {{domxref("WorkerGlobalScope")}}</td> + <td>yes, but is a no-op.</td> + <td>Unknown</td> + <td>no</td> + </tr> + <tr> + <td>{{domxref("DedicatedWorkerGlobalScope.postMessage", "postMessage()")}}</td> + <td>yes, on {{domxref("DedicatedWorkerGlobalScope")}}</td> + <td>no</td> + <td>no</td> + <td>Unknown</td> + <td>no</td> + </tr> + </tbody> +</table> + +<h2 id="APIs_available_in_workers">APIs available in workers</h2> + +<table class="standard-table"> + <tbody> + <tr> + <td class="header">Function</td> + <td class="header">Functionality</td> + <td class="header">Support in Gecko (Firefox)</td> + <td class="header">Support in IE</td> + <td class="header">Support in Blink (Chrome and Opera)</td> + <td class="header">Support in WebKit (Safari)</td> + </tr> + <tr> + <td>{{domxref("Broadcast_Channel_API","Broadcast Channel API")}}</td> + <td> + <p>允许在{{glossary("origin","同源")}}的浏览{{glossary("browsing context", "上下文")}}(即窗口,制表符,框架或iframe)之间进行简单通信(通常是来自同一站点的页面)。</p> + </td> + <td>{{ CompatGeckoDesktop(38)}}</td> + <td><span style="background-color: rgba(212, 221, 228, 0.14902);">{{CompatNo}}</span></td> + <td><span style="background-color: rgba(212, 221, 228, 0.14902);">{{CompatNo}}</span></td> + <td><span style="background-color: rgba(212, 221, 228, 0.14902);">{{CompatNo}}</span></td> + </tr> + <tr> + <td><span style="background-color: rgba(212, 221, 228, 0.14902);">{{domxref("Cache", "Cache")}}</span></td> + <td>Cache API<span style="background-color: rgba(212, 221, 228, 0.14902);"> provides the ability to programmatically</span> control cache storage associated with current origin.</td> + <td><span style="background-color: rgba(212, 221, 228, 0.14902);">{{CompatVersionUnknown}}</span></td> + <td><span style="background-color: rgba(212, 221, 228, 0.14902);">{{CompatNo}}</span></td> + <td><span style="background-color: rgba(212, 221, 228, 0.14902);">{{ CompatChrome(43) }}</span></td> + <td><span style="background-color: rgba(212, 221, 228, 0.14902);">{{CompatUnknown}}</span></td> + </tr> + <tr> + <td><span style="background-color: rgba(212, 221, 228, 0.14902);">{{domxref("Channel_Messaging_API", "Channel Messaging API")}}</span></td> + <td>Allows two separate scripts running in different browsing contexts attached to the same document (e.g., two IFrames, or the main document and an IFrame, two documents via a {{domxref("SharedWorker")}}, or two workers) to communicate directly via two ports.</td> + <td>{{ CompatGeckoDesktop(41)}}</td> + <td>{{CompatVersionUnknown}}</td> + <td>{{CompatVersionUnknown}}</td> + <td>{{CompatVersionUnknown}}</td> + </tr> + <tr> + <td><span style="background-color: rgba(212, 221, 228, 0.14902);">{{domxref("Console", "Console API")}}</span></td> + <td>Provides access to the browser's debugging console (e.g., the <a href="/en-US/docs/Tools/Web_Console">Web Console</a> in Firefox). The specifics of how it works vary from browser to browser, but there is a <em>de facto</em> set of features that are typically provided.</td> + <td>{{ CompatGeckoDesktop(38)}}</td> + <td>{{CompatVersionUnknown}}</td> + <td>{{CompatVersionUnknown}}</td> + <td>{{CompatVersionUnknown}}</td> + </tr> + <tr> + <td><span style="background-color: rgba(212, 221, 228, 0.14902);">{{domxref("CustomEvent")}}</span></td> + <td>The <strong><code>CustomEvent</code></strong> interface represents events initialized by an application for any purpose.</td> + <td>{{ CompatGeckoDesktop(48)}}</td> + <td>{{CompatVersionUnknown}}</td> + <td>{{CompatVersionUnknown}}</td> + <td>{{CompatVersionUnknown}}</td> + </tr> + <tr> + <td>{{domxref("Data_Store_API", "Data Store")}}</td> + <td>A powerful, flexible storage mechanism for multiple Firefox OS applications to use to store and share data between one another quickly, efficiently, and securely.</td> + <td>Only in Firefox OS internal (certified) applications, since v1.0.1.</td> + <td>{{CompatNo}}</td> + <td>{{CompatNo}}</td> + <td>{{CompatNo}}</td> + </tr> + <tr> + <td>{{domxref("DOMRequest")}} and {{domxref("DOMCursor")}}</td> + <td>Respectively, these objects represents an ongoing operation (with listeners for reacting to the operation completely successfully, or failing, for example), and an ongoing operation over a list of results.</td> + <td>{{ CompatGeckoDesktop(41)}}</td> + <td>{{CompatUnknown}}</td> + <td>{{CompatUnknown}}</td> + <td>{{CompatUnknown}}</td> + </tr> + <tr> + <td>{{domxref("Fetch_API", "Fetch")}}</td> + <td>The Fetch spec provides an up-to-date definition of, and API for, fetching resources (e.g. across the network.)</td> + <td>Mostly in {{ CompatGeckoDesktop(34)}} behind pref, although a few features are later.</td> + <td>{{CompatNo}}</td> + <td>{{ CompatChrome(42) }}<br> + {{ CompatChrome(41) }} behind pref</td> + <td>{{CompatNo}}</td> + </tr> + <tr> + <td>{{domxref("FileReader")}}</td> + <td>This API allows asynchronous read of {{domxref("Blob")}} and {{domxref("File")}} objects.</td> + <td>{{CompatGeckoDesktop(46)}}</td> + <td>{{CompatNo}}</td> + <td>{{CompatVersionUnknown}}</td> + <td>{{CompatNo}}</td> + </tr> + <tr> + <td>{{domxref("FileReaderSync")}}</td> + <td>This API allows synchronous read of {{domxref("Blob")}} and {{domxref("File")}} objects. This is an API that works only in workers.</td> + <td>{{CompatGeckoDesktop(8)}}</td> + <td>{{CompatNo}}</td> + <td>{{CompatNo}}</td> + <td>{{CompatNo}}</td> + </tr> + <tr> + <td>{{domxref("FormData")}}</td> + <td><code>FormData</code> objects provide a way to easily construct a set of key/value pairs representing form fields and their values, which can then be easily sent using the XMLHttpRequest <a href="/en-US/docs/DOM/XMLHttpRequest#send()" title="XMLHttpRequest#send()"><code>send()</code></a> method.</td> + <td>{{CompatUnknown}} (should be in {{CompatGeckoDesktop(39)}})</td> + <td>{{CompatUnknown}}</td> + <td>{{CompatVersionUnknown}}</td> + <td>{{CompatUnknown}}</td> + </tr> + <tr> + <td>{{domxref("ImageData")}}</td> + <td>The underlying pixel data of an area of a {{domxref("canvas")}} element. Manipulating such data can be a complex task better suited to be delegated to a Web Worker.</td> + <td>{{CompatGeckoDesktop(25)}}</td> + <td>{{CompatNo}}</td> + <td>{{CompatNo}}</td> + <td>{{CompatNo}}</td> + </tr> + <tr> + <td>{{domxref("IndexedDB_API", "IndexedDB")}}</td> + <td>Database to store records holding simple values and hierarchical objects.</td> + <td>{{CompatGeckoDesktop(37)}}, {{CompatGeckoDesktop(42)}} for {{domxref("IDBCursorWithValue")}}.</td> + <td>10.0</td> + <td>{{CompatVersionUnknown}}</td> + <td>{{CompatNo}}</td> + </tr> + <tr> + <td><a href="/en-US/docs/Web/API/Network_Information_API">Network Information API</a></td> + <td>provides information about the system's connection in terms of general connection type (e.g., 'wifi', 'cellular', etc.).</td> + <td>{{CompatGeckoMobile(53)}} only on mobile</td> + <td>{{CompatVersionUnknown}} only on mobile</td> + <td>{{CompatNo}}</td> + <td>{{CompatNo}}</td> + </tr> + <tr> + <td>{{domxref("Notifications_API", "Notifications")}}</td> + <td>Allows web pages to control the display of system notifications to the end user</td> + <td>{{CompatGeckoDesktop(41)}}</td> + <td>{{CompatUnknown}}</td> + <td>{{CompatUnknown}}</td> + <td>{{CompatUnknown}}</td> + </tr> + <tr> + <td>{{domxref("Performance")}}</td> + <td>The <strong><code>Performance</code></strong> interface represents timing-related performance information for the given page.</td> + <td>{{ CompatGeckoDesktop("34.0") }}</td> + <td>{{CompatUnknown}}</td> + <td>{{ CompatChrome("33.0") }}</td> + <td>{{CompatUnknown}}</td> + </tr> + <tr> + <td>{{jsxref("Promise")}}</td> + <td>JavaScript objects that allow you to write asynchronous functions.</td> + <td>{{CompatGeckoDesktop(28)}}</td> + <td>{{CompatVersionUnknown}}</td> + <td>{{CompatVersionUnknown}}</td> + <td>{{CompatVersionUnknown}}</td> + </tr> + <tr> + <td><a href="/en-US/docs/Web/API/Server-sent_events">Server-sent events</a></td> + <td>Allows a server to push data to a web page at any point, after a connection has been opened to it.</td> + <td>{{CompatGeckoDesktop(53)}} (currently only available in dedicated and shared workers; not service workers.)</td> + <td>{{CompatUnknown}}</td> + <td>{{CompatVersionUnknown}}</td> + <td>{{CompatUnknown}}</td> + </tr> + <tr> + <td>{{domxref("ServiceWorkerRegistration")}}</td> + <td>You can register a service worker from inside a standard worker, and use associated functionality.</td> + <td>{{CompatGeckoDesktop(40)}}</td> + <td>{{CompatNo}}</td> + <td>{{CompatVersionUnknown}}</td> + <td>{{CompatNo}}</td> + </tr> + <tr> + <td>{{domxref("TextEncoder")}} and {{domxref("TextDecoder")}}</td> + <td>Create and return a new {{domxref("TextEncoder")}}, or respectively {{domxref("TextDecoder")}}, allowing to encode or decode strings into specific encodings.</td> + <td>{{CompatGeckoDesktop(20)}}</td> + <td>{{CompatNo}}</td> + <td>{{CompatNo}}</td> + <td>{{CompatNo}}</td> + </tr> + <tr> + <td>{{ domxref("URL") }}</td> + <td>Workers can use the static methods <a href="/en-US/docs/DOM/window.URL.createObjectURL" title="/en-US/docs/DOM/window.URL.createObjectURL">URL.createObjectURL</a> and <a href="/en-US/docs/DOM/window.URL.revokeObjectURL" title="/en-US/docs/DOM/window.URL.revokeObjectURL">URL.revokeObjectURL</a> with {{domxref("Blob")}} objects accesible to the worker.<br> + Workers can also create a new URL using the {{domxref("URL.URL", "URL()")}} constructor and call any normal method on the returned object.</td> + <td>{{CompatGeckoDesktop(21)}} and {{CompatGeckoDesktop(26)}} for URL() constructor</td> + <td>{{CompatNo}}</td> + <td>{{CompatNo}}</td> + <td>{{CompatNo}}</td> + </tr> + <tr> + <td><a href="/en-US/docs/Web/API/WebGL_API">WebGL</a> with {{domxref("OffscreenCanvas")}}</td> + <td>WebGL (Web Graphics Library) is a JavaScript API for rendering interactive 3D and 2D graphics within any compatible web browser without the use of plug-ins.</td> + <td>{{CompatGeckoDesktop(44)}} behind a feature preference setting. In <code>about:config</code>, set <code>gfx.offscreencanvas.enabled</code> to true.</td> + <td>{{CompatNo}}</td> + <td>{{CompatNo}}</td> + <td>{{CompatNo}}</td> + </tr> + <tr> + <td>{{domxref("WebSocket")}}</td> + <td>Creates and returns a new {{domxref("WebSocket")}} object; this mimics the behavior of the standard <code>WebSocket()</code> constructor.</td> + <td>{{CompatGeckoDesktop(37)}}</td> + <td>11.0</td> + <td>{{CompatVersionUnknown}}</td> + <td>{{CompatVersionUnknown}}</td> + </tr> + <tr> + <td>{{domxref("Worker")}}</td> + <td>Creates a new {{ domxref("Worker") }}. Yes, workers can spawn more workers.</td> + <td>{{CompatGeckoDesktop("1.9.1")}}</td> + <td>10.0</td> + <td>{{CompatNo}} See <a class="external" href="https://code.google.com/p/chromium/issues/detail?id=31666" rel="external" title="http://code.google.com/p/chromium/issues/detail?id=31666">crbug.com/31666</a></td> + <td>{{CompatNo}}</td> + </tr> + <tr> + <td>{{domxref("WorkerGlobalScope")}}</td> + <td>The global scope of workers. This objects defines <a href="#workerscope">worker-specific functions</a>.</td> + <td>{{CompatVersionUnknown}}</td> + <td>10.0</td> + <td>{{CompatVersionUnknown}}</td> + <td>{{CompatVersionUnknown}}</td> + </tr> + <tr> + <td>{{domxref("WorkerLocation")}}</td> + <td>The subset of the {{domxref("Location")}} interface available to workers.</td> + <td>{{CompatGeckoDesktop(1.9.2)}}</td> + <td>10.0</td> + <td>{{CompatVersionUnknown}}</td> + <td>{{CompatVersionUnknown}}</td> + </tr> + <tr> + <td>{{domxref("WorkerNavigator")}}</td> + <td>The subset of the {{domxref("Navigator")}} interface available to workers.</td> + <td>Basic implementation {{CompatVersionUnknown}}<br> + {{domxref("NavigatorID.appCodeName", "appCodeName")}}, {{domxref("NavigatorID.product", "product")}}, {{domxref("NavigatorID.taintEnabled", "taintEnabled()")}}: {{CompatGeckoDesktop(28)}}<br> + {{domxref("WorkerNavigator.onLine", "onLine")}}: {{CompatGeckoDesktop(29)}}<br> + {{domxref("NavigatorLanguage")}}: {{CompatVersionUnknown}}</td> + <td>{{domxref("NavigatorID.appName", "appName")}}, {{domxref("NavigatorID.appVersion", "appVersion")}}, {{domxref("WorkerNavigator.onLine", "onLine")}}, {{domxref("NavigatorID.platform", "platform")}}, {{domxref("NavigatorID.userAgent", "userAgent")}}: 10.0<br> + Other: {{CompatNo}}</td> + <td>{{CompatVersionUnknown}}</td> + <td>{{CompatVersionUnknown}}</td> + </tr> + <tr> + <td>{{domxref("XMLHttpRequest")}}</td> + <td>Creates and returns a new {{domxref("XMLHttpRequest")}} object; this mimics the behavior of the standard <code>XMLHttpRequest()</code> constructor. Note that the <code>responseXML</code> and <code>channel</code> attributes on <code>XMLHttpRequest</code> always return <code>null</code>.</td> + <td> + <p>Basic: {{CompatGeckoDesktop("1.9.1")}}</p> + + <p>{{domxref("XMLHttpRequest.response", "response")}} and {{domxref("XMLHttpRequest.responseType", "responseType")}} are available since {{CompatGeckoDesktop("10")}}</p> + + <p>{{domxref("XMLHttpRequest.timeout", "timeout")}} and {{domxref("XMLHttpRequest.ontimeout", "ontimeout")}} are available since {{CompatGeckoDesktop("13")}}</p> + </td> + <td>{{CompatVersionUnknown}}</td> + <td>{{CompatVersionUnknown}}</td> + <td>{{CompatVersionUnknown}}</td> + </tr> + </tbody> +</table> + +<h2 id="See_also">See also</h2> + +<ul> + <li><a href="/en-US/docs/Web/API/Web_Workers_API/Using_web_workers">Using web workers</a></li> + <li>{{domxref("Worker")}}</li> +</ul> diff --git a/files/zh-cn/web/api/web_workers_api/index.html b/files/zh-cn/web/api/web_workers_api/index.html new file mode 100644 index 0000000000..d9ee40bbaa --- /dev/null +++ b/files/zh-cn/web/api/web_workers_api/index.html @@ -0,0 +1,102 @@ +--- +title: Web Workers API +slug: Web/API/Web_Workers_API +tags: + - API + - DOM + - Service Workers + - Shared Workers + - Web Workers + - Workers +translation_of: Web/API/Web_Workers_API +--- +<p>{{DefaultAPISidebar("Web Workers API")}}</p> + +<p class="summary">通过使用Web Workers,Web应用程序可以在独立于主线程的后台线程中,运行一个脚本操作。这样做的好处是可以在独立线程中执行费时的处理任务,从而允许主线程(通常是UI线程)不会因此被阻塞/放慢。</p> + +<h2 id="Web_Workers_概念与用法">Web Workers 概念与用法</h2> + +<p>使用构造函数(例如,{{domxref("Worker.Worker", "Worker()")}})创建一个<strong> worker</strong> 对象, 构造函数接受一个 JavaScript文件URL — 这个文件包含了将在 worker 线程中运行的代码。worker 将运行在与当前 {{domxref("window")}}不同的另一个全局上下文中,这个上下文由一个对象表示,标准情况下为{{domxref("DedicatedWorkerGlobalScope")}} (标准 workers 由单个脚本使用; 共享workers使用{{domxref("SharedWorkerGlobalScope")}})。</p> + +<p>你可以在worker 线程中运行任意的代码,但注意存在一些例外:直接在 worker 线程中操纵 DOM 元素;或使用{{domxref("window")}} 对象中的某些方法和属性。大部分 window 对象的方法和属性是可以使用的,包括 <a href="/en-US/docs/WebSockets">WebSockets</a>,以及诸如 <a href="/en-US/docs/Web/API/IndexedDB_API">IndexedDB</a> 和 FireFox OS 中独有的 <a href="/en-US/docs/Web/API/Data_Store_API">Data Store API</a> 这一类数据存储机制。更多信息请参见: <a href="/en-US/docs/Web/API/Worker/Functions_and_classes_available_to_workers">Functions and classes available to workers</a> 。</p> + +<p>主线程和 worker 线程相互之间使用 <code>postMessage()</code> 方法来发送信息, 并且通过 <code>onmessage</code> 这个 event handler来接收信息(传递的信息包含在 {{event("Message")}} 这个事件的<code>data</code>属性内) 。数据的交互方式为传递副本,而不是直接共享数据。</p> + +<p>worker 可以另外生成新的 worker,这些 worker 与它们父页面的宿主相同。 此外,worker 可以通过 <a class="internal" href="/en/DOM/XMLHttpRequest" title="En/XMLHttpRequest"><code>XMLHttpRequest</code></a> 来访问网络,只不过 <code>XMLHttpRequest</code> 的 <code>responseXML</code> 和 <code>channel</code> 这两个属性的值将总是 <code>null 。</code></p> + +<p>除了专用 worker 之外,还有一些其他种类的 worker :</p> + +<ul> + <li>Shared Workers 可被不同的窗体的多个脚本运行,例如IFrames等,只要这些workers处于同一主域。共享worker 比专用 worker 稍微复杂一点 — 脚本必须通过活动端口进行通讯。详情请见{{domxref("SharedWorker")}}。</li> + <li><a href="/zh-CN/docs/Web/API/Service_Worker_API">Service Workers</a> 一般作为web应用程序、浏览器和网络(如果可用)之间的代理服务。他们旨在(除开其他方面)创建有效的离线体验,拦截网络请求,以及根据网络是否可用采取合适的行动,更新驻留在服务器上的资源。他们还将允许访问推送通知和后台同步API。</li> + <li>Chrome Workers 是一种仅适用于firefox的worker。如果您正在开发附加组件,希望在扩展程序中使用worker且可以访问 <a href="https://developer.mozilla.org/en/js-ctypes" title="en/js-ctypes">js-ctypes</a>,那么可以使用Chrome Workers。详情请见{{domxref("ChromeWorker")}}</li> + <li>音频<a href="/en-US/docs/Web/API/Web_Audio_API#Audio_Workers"> Workers</a>可以在网络worker上下文中直接完成脚本化音频处理.</li> +</ul> + +<div class="note"> +<p><span style="font-size: 14px;"><strong>注意</strong></span>: 根据网络worker规范, worker错误事件不应该冒泡(参见{{bug(1188141)}})。该规范已在Firefox 42中实现。</p> +</div> + +<h2 id="Web_Worker_接口">Web Worker 接口</h2> + +<dl> + <dt>{{domxref("AbstractWorker")}}</dt> + <dd>抽象属性和方法是所有类型的worker中常用的(例如{{domxref("Worker")}}或 {{domxref("SharedWorker")}})</dd> + <dt>{{domxref("Worker")}}</dt> + <dd>表示正在运行的worker线程,允许你将信息传递到正在运行的worker程序代码。</dd> + <dt>{{domxref("SharedWorker")}}</dt> + <dd>表示一种可以同时被多个浏览器环境访问的特殊类型的worker。这些浏览器环境可以是多个window, iframes 或者甚至是多个worker.</dd> + <dt>{{domxref("WorkerGlobalScope")}}</dt> + <dd>表示任意worker的通用作用域(对于正常的网页类容来说与{{domxref("Window")}} 有相同的作用)。不同类型的worker都有从接口继承作用于对象,并且可以添加更多特定功能。</dd> + <dt>{{domxref("DedicatedWorkerGlobalScope")}}</dt> + <dd>表示一个专用worker的作用域, 继承自{{domxref("WorkerGlobalScope")}},且可添加一些特有的功能。</dd> + <dt>{{domxref("SharedWorkerGlobalScope")}}</dt> + <dd>表示一个共享worker的作用域, 继承自{{domxref("WorkerGlobalScope")}}, 且可添加一些特有的功能。</dd> + <dt>{{domxref("WorkerNavigator")}}</dt> + <dd>表示用户代理(客户端)的身份和状态。</dd> +</dl> + +<h2 id="示例">示例</h2> + +<p>我们创建了几个简单的demos以演示基本用法:</p> + +<ul> + <li><a href="https://github.com/mdn/simple-web-worker">基本的dedicated worker示例</a> (<a href="http://mdn.github.io/simple-web-worker/">运行dedicated worker</a>).</li> + <li><a href="https://github.com/mdn/simple-shared-worker">基本的 shared worker示例</a> (<a href="http://mdn.github.io/simple-shared-worker/">运行shared worker</a>).</li> +</ul> + +<p>你可以在<a href="/en-US/docs/Web/API/Web_Workers_API/Using_web_workers">使用web workers</a>中找到有关这些demos是如何工作的更多信息。</p> + +<h2 id="规范">规范</h2> + +<table class="standard-table"> + <tbody> + <tr> + <th scope="col">规范</th> + <th scope="col">状态</th> + <th scope="col">评论</th> + </tr> + <tr> + <td>{{SpecName('HTML WHATWG', '#toc-workers')}}</td> + <td>{{Spec2('HTML WHATWG')}}</td> + <td>No change from {{SpecName("Web Workers")}}.</td> + </tr> + <tr> + <td>{{SpecName('Web Workers')}}</td> + <td>{{Spec2('Web Workers')}}</td> + <td>Initial definition.</td> + </tr> + </tbody> +</table> + +<h2 id="相关链接"><span style="font-size: 2.33333rem; letter-spacing: -0.00278rem;">相关链接</span></h2> + +<ul> + <li><a href="/en-US/docs/Web/API/Web_Workers_API/basic_usage">Using Web Workers</a></li> + <li><a href="https://developer.mozilla.org/en-US/docs/Web/API/Worker">Worker Interface</a></li> + <li><a href="https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker">SharedWorker interface</a></li> + <li><a href="https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorker_API">ServiceWorker API</a></li> + <li><a href="/en-US/docs/Web/API/Worker/Functions_and_classes_available_to_workers">Functions and classes available to workers</a></li> + <li><a href="/en-US/docs/Web/API/Web_Workers_API/Advanced_concepts_and_examples">Advanced concepts and examples</a></li> + <li><a href="/en-US/docs/Web/API/ChromeWorker">ChromeWorker</a>: for using workers in privileged/chrome code</li> +</ul> diff --git a/files/zh-cn/web/api/web_workers_api/using_web_workers/index.html b/files/zh-cn/web/api/web_workers_api/using_web_workers/index.html new file mode 100644 index 0000000000..107266e073 --- /dev/null +++ b/files/zh-cn/web/api/web_workers_api/using_web_workers/index.html @@ -0,0 +1,794 @@ +--- +title: 使用 Web Workers +slug: Web/API/Web_Workers_API/Using_web_workers +tags: + - Advanced + - Firefox + - Guide + - HTML5 + - JavaScript + - Web Workers + - Workers +translation_of: Web/API/Web_Workers_API/Using_web_workers +--- +<div>{{DefaultAPISidebar("Web Workers API")}}</div> + +<p class="summary">Web Worker为Web内容在后台线程中运行脚本提供了一种简单的方法。线程可以执行任务而不干扰用户界面。此外,他们可以使用<code><a href="https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIXMLHttpRequest">XMLHttpRequest</a></code>执行 I/O (尽管<code>responseXML</code>和<code>channel</code>属性总是为空)。一旦创建, 一个worker 可以将消息发送到创建它的JavaScript代码, 通过将消息发布到该代码指定的事件处理程序(反之亦然)。本文提供了有关使用Web Worker的详细介绍。</p> + +<h2 id="Web_Workers_API">Web Workers API</h2> + +<p>一个worker是使用一个构造函数创建的一个对象(e.g. {{domxref("Worker.Worker", "Worker()")}}) 运行一个命名的JavaScript文件 - 这个文件包含将在工作线程中运行的代码; workers 运行在另一个全局上下文中,不同于当前的{{domxref("window")}}. 因此,在 {{domxref("Worker")}} 内通过 {{domxref("window")}}获取全局作用域 (而不是{{domxref("window.self","self")}}) 将返回错误。</p> + +<p>在专用workers的情况下,{{domxref("DedicatedWorkerGlobalScope")}} 对象代表了worker的上下文(专用workers是指标准worker仅在单一脚本中被使用;共享worker的上下文是{{domxref("SharedWorkerGlobalScope")}}对象)。一个专用worker仅仅能被首次生成它的脚本使用,而共享worker可以同时被多个脚本使用。</p> + +<div class="note"><strong>注意</strong>:参照 <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API">The Web Workers API landing page</a> 获取workers的参考文档和更多指引。</div> + +<p>在worker线程中你可以运行任何你喜欢的代码,不过有一些例外情况。比如:在worker内,不能直接操作DOM节点,也不能使用{{domxref("window")}}对象的默认方法和属性。然而你可以使用大量window对象之下的东西,包括WebSockets,IndexedDB以及FireFox OS专用的Data Store API等数据存储机制。查看<a href="https://developer.mozilla.org/en-US/docs/Web/API/Worker/Functions_and_classes_available_to_workers">Functions and classes available to workers</a>获取详情。</p> + +<p>workers和主线程间的数据传递通过这样的消息机制进行——双方都使用postMessage()方法发送各自的消息,使用onmessage事件处理函数来响应消息(消息被包含在{{event("Message")}}事件的data属性中)。这个过程中数据并不是被共享而是被复制。</p> + +<p>只要运行在同源的父页面中,workers可以依次生成新的workers;并且可以使用<a href="https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest"><code>XMLHttpRequest</code></a> 进行网络I/O,但是XMLHttpRequest的responseXML和channel属性总会返回null。</p> + +<h2 id="专用worker">专用worker</h2> + +<p>如前文所述,一个专用worker仅仅能被生成它的脚本所使用。这一部分将探讨<a href="https://github.com/mdn/simple-web-worker"> 专用worker基础示例</a> (<a href="https://mdn.github.io/simple-web-worker/">运行专用worker</a>) 中的JavaScript代码:将你输入的2个数字作乘法。输入的数字会发送给一个专用worker,由专用worker作乘法后,再返回给页面进行展示。</p> + +<p>这个例子很小,但是我们决定在保持简单的同时向你介绍基础的worker概念。更多的细节会在之后的文章中进行讲解。</p> + +<h3 id="worker特性检测">worker特性检测</h3> + +<p>为了更好的错误处理控制以及向下兼容,将你的worker运行代码包裹在以下代码中是一个很好的想法(main.js):</p> + +<pre class="brush: js notranslate">if (window.Worker) { + + ... + +}</pre> + +<h3 id="生成一个专用worker">生成一个专用worker</h3> + +<p>创建一个新的worker很简单。你需要做的是调用{{domxref("Worker.Worker", "Worker()")}} 的构造器,指定一个脚本的URI来执行worker线程(main.js):</p> + +<pre class="brush: js notranslate">var myWorker = new Worker('worker.js');</pre> + +<h3 id="专用worker中消息的接收和发送">专用worker中消息的接收和发送</h3> + +<p>你可以通过{{domxref("Worker.postMessage", "postMessage()")}} 方法和{{domxref("Worker.onmessage", "onmessage")}}事件处理函数触发workers的方法。当你想要向一个worker发送消息时,你只需要这样做(main.js):</p> + +<pre class="brush: js notranslate">first.onchange = function() { + myWorker.postMessage([first.value,second.value]); + console.log('Message posted to worker'); +} + +second.onchange = function() { + myWorker.postMessage([first.value,second.value]); + console.log('Message posted to worker'); +}</pre> + +<p>这段代码中变量first和second代表2个{{htmlelement("input")}}元素;它们当中任意一个的值发生改变时,myWorker.postMessage([first.value,second.value])会将这2个值组成数组发送给worker。你可以在消息中发送许多你想发送的东西。</p> + +<p>在worker中接收到消息后,我们可以写这样一个事件处理函数代码作为响应(worker.js):</p> + +<pre class="brush: js notranslate">onmessage = function(e) { + console.log('Message received from main script'); + var workerResult = 'Result: ' + (e.data[0] * e.data[1]); + console.log('Posting message back to main script'); + postMessage(workerResult); +}</pre> + +<p>onmessage处理函数允许我们在任何时刻,一旦接收到消息就可以执行一些代码,代码中消息本身作为事件的data属性进行使用。这里我们简单的对这2个数字作乘法处理并再次使用postMessage()方法,将结果回传给主线程。</p> + +<p>回到主线程,我们再次使用onmessage以响应worker回传的消息:</p> + +<pre class="brush: js notranslate">myWorker.onmessage = function(e) { + result.textContent = e.data; + console.log('Message received from worker'); +}</pre> + +<p>在这里我们获取消息事件的data,并且将它设置为result的textContent,所以用户可以直接看到运算的结果。</p> + +<div class="note"> +<p><strong>注意:</strong> 在主线程中使用时,<code>onmessage</code>和<code>postMessage()</code> 必须挂在worker对象上,而在worker中使用时不用这样做。原因是,在worker内部,worker是有效的全局作用域。</p> +</div> + +<div class="note"> +<p><strong>注意:</strong> 当一个消息在主线程和worker之间传递时,它被复制或者转移了,而不是共享。请参阅{{anch("Transferring data to and from workers further details", "Transferring data to and from workers: further details")}} 获取更详尽的解释。</p> +</div> + +<h3 id="终止worker">终止worker</h3> + +<p>如果你需要从主线程中立刻终止一个运行中的worker,可以调用worker的{{domxref("Worker", "terminate")}} 方法:</p> + +<pre class="brush: js notranslate">myWorker.terminate();</pre> + +<p>worker 线程会被立即杀死,不会有任何机会让它完成自己的操作或清理工作。</p> + +<p>而在worker线程中,workers 也可以调用自己的 {{domxref("WorkerGlobalScope", "close")}} 方法进行关闭:</p> + +<pre class="brush: js notranslate"><code>close();</code></pre> + +<h3 id="处理错误">处理错误</h3> + +<p>当 worker 出现运行中错误时,它的 <code>onerror</code> 事件处理函数会被调用。它会收到一个扩展了 <code>ErrorEvent</code> 接口的名为 <code>error</code>的事件。</p> + +<p>该事件不会冒泡并且可以被取消;为了防止触发默认动作,worker 可以调用错误事件的 <code><a class="internal" href="/zh-CN/docs/Web/API/Event/preventDefault">preventDefault()</a></code>方法。</p> + +<p>错误事件有以下三个用户关心的字段:</p> + +<dl> + <dt><code>message</code></dt> + <dd>可读性良好的错误消息。</dd> + <dt><code>filename</code></dt> + <dd>发生错误的脚本文件名。</dd> + <dt><code>lineno</code></dt> + <dd>发生错误时所在脚本文件的行号。</dd> +</dl> + +<h3 id="生成subworker">生成subworker</h3> + +<p>如果需要的话 worker 能够生成更多的 worker。这就是所谓的subworker,它们必须托管在同源的父页面内。而且,subworker 解析 URI 时会相对于父 worker 的地址而不是自身页面的地址。这使得 worker 更容易记录它们之间的依赖关系。</p> + +<h3 id="引入脚本与库">引入脚本与库</h3> + +<p>Worker 线程能够访问一个全局函数<code>importScripts()</code>来引入脚本,该函数接受0个或者多个URI作为参数来引入资源;以下例子都是合法的:</p> + +<pre class="brush: js notranslate">importScripts(); /* 什么都不引入 */ +importScripts('foo.js'); /* 只引入 "foo.js" */ +importScripts('foo.js', 'bar.js'); /* 引入两个脚本 */ +</pre> + +<p>浏览器加载并运行每一个列出的脚本。每个脚本中的全局对象都能够被 worker 使用。如果脚本无法加载,将抛出 <code>NETWORK_ERROR</code> 异常,接下来的代码也无法执行。而之前执行的代码(包括使用 {{ domxref("window.setTimeout()") }} 异步执行的代码)依然能够运行。<code>importScripts()</code> <strong>之后</strong>的函数声明依然会被保留,因为它们始终会在其他代码之前运行。</p> + +<div class="note"><strong>注意:</strong> 脚本的下载顺序不固定,但执行时会按照传入 <code>importScripts()</code> 中的文件名顺序进行。这个过程是同步完成的;直到所有脚本都下载并运行完毕,<code>importScripts()</code> 才会返回。</div> + +<h2 id="共享worker">共享worker</h2> + +<p>一个共享worker可以被多个脚本使用——即使这些脚本正在被不同的window、iframe或者worker访问。这一部分,我们会讨论<a href="https://github.com/mdn/simple-shared-worker">共享worker基础示例</a>(<a href="https://mdn.github.io/simple-shared-worker/">运行共享worker</a>)中的javascript代码:该示例与专用worker基础示例非常相像,只是有2个可用函数被存放在不同脚本文件中:两数相乘函数,以及求平方函数。这两个脚本用同一个worker来完成实际需要的运算。</p> + +<p>这里,我们关注一下专用worker和共享worker之间的区别。在这个示例中有2个HTML页面,每个页面所包含的javascript代码使用的是同一个worker。</p> + +<div class="note"> +<p><strong>注意:</strong>如果共享worker可以被多个浏览上下文调用,所有这些浏览上下文必须属于同源(相同的协议,主机和端口号)。 </p> +</div> + +<div class="note"> +<p><strong>注意:</strong>在 Firefox中, 共享worker不能被私有和非私有window对象的document所共享 ({{bug(1177621)}})。</p> +</div> + +<h3 id="生成一个共享worker">生成一个共享worker</h3> + +<p>生成一个新的共享worker与生成一个专用worker非常相似,只是构造器的名字不同(查看 <a href="https://github.com/mdn/simple-shared-worker/blob/gh-pages/index.html">index.html</a> 和 <a href="https://mdn.github.io/simple-shared-worker/index2.html">index2.html</a>)——生成共享worker的代码如下:</p> + +<pre class="brush: js notranslate">var myWorker = new SharedWorker('worker.js');</pre> + +<p>一个非常大的区别在于,与一个共享worker通信必须通过端口对象——一个确切的打开的端口供脚本与worker通信(在专用worker中这一部分是隐式进行的)。</p> + +<p>在传递消息之前,端口连接必须被显式的打开,打开方式是使用onmessage事件处理函数或者start()方法。示例中的 <a href="https://github.com/mdn/simple-shared-worker/blob/gh-pages/multiply.js">multiply.js</a> 和 <a href="https://github.com/mdn/simple-shared-worker/blob/gh-pages/worker.js">worker.js</a> 文件没有调用了start()方法,这些调用并不那么重要是因为onmessage事件处理函数正在被使用。start()方法的调用只在一种情况下需要,那就是消息事件被addEventListener()方法使用。</p> + +<p>在使用start()方法打开端口连接时,如果父级线程和worker线程需要双向通信,那么它们都需要调用start()方法。</p> + +<pre class="brush: js notranslate">myWorker.port.start(); // 父级线程中的调用</pre> + +<pre class="brush: js notranslate">port.start(); // worker线程中的调用, 假设port变量代表一个端口</pre> + +<h3 id="共享worker中消息的接收和发送">共享worker中消息的接收和发送</h3> + +<p>现在,消息可以像之前那样发送到worker了,但是<code>postMessage()</code> 方法必须被端口对象调用(你会再一次看到 <a href="https://github.com/mdn/simple-shared-worker/blob/gh-pages/multiply.js">multiply.js</a> 和 <a href="https://github.com/mdn/simple-shared-worker/blob/gh-pages/square.js">square.js</a>中相似的结构):</p> + +<pre class="brush: js notranslate">squareNumber.onchange = function() { + myWorker.port.postMessage([squareNumber.value,squareNumber.value]); + console.log('Message posted to worker'); +}</pre> + +<p>回到worker中,这里也有一些些复杂(<a href="https://github.com/mdn/simple-shared-worker/blob/gh-pages/worker.js">worker.js</a>):</p> + +<pre class="brush: js notranslate">onconnect = function(e) { + var port = e.ports[0]; + + port.onmessage = function(e) { + var workerResult = 'Result: ' + (e.data[0] * e.data[1]); + port.postMessage(workerResult); + } +}</pre> + +<p>首先,当一个端口连接被创建时(例如:在父级线程中,设置onmessage事件处理函数,或者显式调用start()方法时),使用onconnect事件处理函数来执行代码。</p> + +<p>使用事件的ports属性来获取端口并存储在变量中。</p> + +<p>然后,为端口添加一个消息处理函数用来做运算并回传结果给主线程。在worker线程中设置此消息处理函数也会隐式的打开与主线程的端口连接,因此这里跟前文一样,对port.start()的调用也是不必要的。</p> + +<p>最后,回到主脚本,我们处理消息(你会又一次看到 <a href="https://github.com/mdn/simple-shared-worker/blob/gh-pages/multiply.js">multiply.js</a> 和 <a href="https://github.com/mdn/simple-shared-worker/blob/gh-pages/square.js">square.js</a>中相似的结构):</p> + +<pre class="brush: js notranslate">myWorker.port.onmessage = function(e) { + result2.textContent = e.data; + console.log('Message received from worker'); +}</pre> + +<p>当一条消息通过端口回到worker,我们检查结果的类型,然后将运算结果放入结果段落中合适的地方。</p> + +<h2 id="关于线程安全">关于线程安全</h2> + +<p>{{domxref("Worker")}}接口会生成真正的操作系统级别的线程,如果你不太小心,那么并发会对你的代码产生有趣的影响。然而,对于 web worker 来说,与其他线程的通信点会被很小心的控制,这意味着你很难引起并发问题。你没有办法去访问非线程安全的组件或者是 DOM,此外你还需要通过序列化对象来与线程交互特定的数据。所以你要是不费点劲儿,还真搞不出错误来。</p> + +<h2 id="内容安全策略">内容安全策略</h2> + +<p>有别于创建它的document对象,worker有它自己的执行上下文。因此普遍来说,worker并不受限于创建它的document(或者父级worker)的内容安全策略。我们来举个例子,假设一个document有如下头部声明:</p> + +<pre class="notranslate">Content-Security-Policy: script-src 'self'</pre> + +<p>这个声明有一部分作用在于,禁止它内部包含的脚本代码使用eval()方法。然而,如果脚本代码创建了一个worker,在worker上下文中执行的代码却是可以使用eval()的。</p> + +<p>为了给worker指定内容安全策略,必须为发送worker代码的请求本身加上一个 <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy">内容安全策略</a>。</p> + +<p>有一个例外情况,即worker脚本的源如果是一个全局性的唯一的标识符(例如,它的URL指定了数据模式或者blob),worker则会继承创建它的document或者worker的CSP(Content security policy内容安全策略)。</p> + +<h2 id="worker中数据的接收与发送:详细介绍">worker中数据的接收与发送:详细介绍</h2> + +<p>在主页面与 worker 之间传递的数据是通过<strong>拷贝</strong>,而不是共享来完成的。传递给 <code>worker </code>的对象需要经过序列化,接下来在另一端还需要反序列化。页面与<code> worker </code><strong>不会共享同一个实例,</strong>最终的结果就是在每次通信结束时生成了数据的<strong>一个副本。</strong>大部分浏览器使用<a href="/en/DOM/The_structured_clone_algorithm">结构化拷贝</a>来实现该特性。</p> + +<p>在往下进行之前,出于教学的目的,让我们创建一个名为 <code>emulateMessage()</code> 的函数,它将模拟在从 <code>worker</code> 到主页面(反之亦然)的通信过程中,变量的「<em>拷贝而非共享</em>」行为:</p> + +<pre class="brush: js notranslate">function emulateMessage (vVal) { + return eval("(" + JSON.stringify(vVal) + ")"); +} + +// Tests + +// test #1 +var example1 = new Number(3); +alert(typeof example1); // object +alert(typeof emulateMessage(example1)); // number + +// test #2 +var example2 = true; +alert(typeof example2); // boolean +alert(typeof emulateMessage(example2)); // boolean + +// test #3 +var example3 = new String("Hello World"); +alert(typeof example3); // object +alert(typeof emulateMessage(example3)); // string + +// test #4 +var example4 = { + "name": "John Smith", + "age": 43 +}; +alert(typeof example4); // object +alert(typeof emulateMessage(example4)); // object + +// test #5 +function Animal (sType, nAge) { + this.type = sType; + this.age = nAge; +} +var example5 = new Animal("Cat", 3); +alert(example5.constructor); // Animal +alert(emulateMessage(example5).constructor); // Object</pre> + +<p>拷贝而并非共享的那个值称为 <em>消息</em>。再来谈谈 <code>worker,你可以使用 </code>postMessage() 将消息传递给主线程或从主线程传送回来。<code>message</code> 事件的 <code>data</code> 属性就包含了从 worker 传回来的数据。</p> + +<p><strong>example.html</strong>: (主页面):</p> + +<pre class="notranslate">var myWorker = new Worker("my_task.js"); + +myWorker.onmessage = function (oEvent) { + console.log("Worker said : " + oEvent.data); +}; + +myWorker.postMessage("ali");</pre> + +<p><strong>my_task.js</strong> (worker):</p> + +<pre class="notranslate">postMessage("I\'m working before postMessage(\'ali\')."); + +onmessage = function (oEvent) { + postMessage("Hi " + oEvent.data); +}; +</pre> + +<p><a href="https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/The_structured_clone_algorithm">结构化拷贝</a>算法可以接收JSON数据以及一些JSON不能表示的数据——比如循环引用。</p> + +<h3 id="传递数据的例子">传递数据的例子</h3> + +<h4 id="例子_1:_创建一个通用的_「异步_eval」">例子 #1: 创建一个通用的 「异步 <a href="/en-US/docs/JavaScript/Reference/Global_Objects/eval"><code>eval()</code></a>」</h4> + +<p>下面这个例子介绍了,如何在 worker 内使用 <code><a href="/en-US/docs/JavaScript/Reference/Global_Objects/eval">eval()</a> </code>来按顺序执行<strong>异步的</strong>任何种类的 JavaScript 代码:</p> + +<pre class="brush: js notranslate">// Syntax: asyncEval(code[, listener]) + +var asyncEval = (function () { + + var aListeners = [], oParser = new Worker("data:text/javascript;charset=US-ASCII,onmessage%20%3D%20function%20%28oEvent%29%20%7B%0A%09postMessage%28%7B%0A%09%09%22id%22%3A%20oEvent.data.id%2C%0A%09%09%22evaluated%22%3A%20eval%28oEvent.data.code%29%0A%09%7D%29%3B%0A%7D"); + + oParser.onmessage = function (oEvent) { + if (aListeners[oEvent.data.id]) { aListeners[oEvent.data.id](oEvent.data.evaluated); } + delete aListeners[oEvent.data.id]; + }; + + + return function (sCode, fListener) { + aListeners.push(fListener || null); + oParser.postMessage({ + "id": aListeners.length - 1, + "code": sCode + }); + }; + +})();</pre> + +<p><a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/data_URIs">data URL</a> 相当于一个网络请求,它有如下返回:</p> + +<pre class="notranslate">onmessage = function(oEvent) { + postMessage({ + 'id': oEvent.data.id, + 'evaluated': eval(oEvent.data.code) + }); +}</pre> + +<p>示例使用:</p> + +<pre class="brush: js notranslate">// asynchronous alert message... +asyncEval("3 + 2", function (sMessage) { + alert("3 + 2 = " + sMessage); +}); + +// asynchronous print message... +asyncEval("\"Hello World!!!\"", function (sHTML) { + document.body.appendChild(document.createTextNode(sHTML)); +}); + +// asynchronous void... +asyncEval("(function () {\n\tvar oReq = new XMLHttpRequest();\n\toReq.open(\"get\", \"http://www.mozilla.org/\", false);\n\toReq.send(null);\n\treturn oReq.responseText;\n})()");</pre> + +<h4 id="例子_2:传输_JSON_的高级方式和创建一个交换系统">例子 #2:传输 JSON 的高级方式和创建一个交换系统</h4> + +<p>如果你需要传输非常复杂的数据,还要同时在主页与 Worker 内调用多个方法,那么可以考虑创建一个类似下面的系统。</p> + +<p>首先,我们创建一个QueryableWorker的类,它接收worker的url、一个默认侦听函数、和一个错误处理函数作为参数,这个类将会记录所有的侦听的列表并且帮助我们与worker进行通信。</p> + +<pre class="notranslate">function QueryableWorker(url, defaultListener, onError) { + var instance = this, + worker = new Worker(url), + listeners = {}; + + this.defaultListener = defaultListener || function() {}; + + if (onError) {worker.onerror = onError;} + + this.postMessage = function(message) { + worker.postMessage(message); + } + + this.terminate = function() { + worker.terminate(); + } +}</pre> + +<p>紧接着,我们写出新增和删除侦听的方法。</p> + +<pre class="notranslate">this.addListeners = function(name, listener) { + listeners[name] = listener; +} + +this.removeListeners = function(name) { + delete listeners[name]; +}</pre> + +<p>这里我们让worker处理2个这样的简单操作:区别2个数字并在3秒后弹框提示。为了完成这个操作,我们首先实现一个sendQuery方法,该方法可以查询worker是否真正有我们所需要的对应方法。</p> + +<pre class="notranslate">/* + This functions takes at least one argument, the method name we want to query. + Then we can pass in the arguments that the method needs. + */ +this.sendQuery = function() { + if (arguments.length < 1) { + throw new TypeError('QueryableWorker.sendQuery takes at least one argument'); + return; + } + worker.postMessage({ + 'queryMethod': arguments[0], + 'queryArguments': Array.prototype.slice.call(arguments, 1) + }); +}</pre> + +<p>我们以<code>onmessage方法作为</code>QueryableWorker的结尾。如果worker有我们所需要的对应的方法,它就会返回相对应的侦听方法的名字以及所需要的参数,我们只需要在侦听列表<code>listeners中找到它:</code></p> + +<pre class="notranslate">worker.onmessage = function(event) { + if (event.data instanceof Object && + event.data.hasOwnProperty('queryMethodListener') && + event.data.hasOwnProperty('queryMethodArguments')) { + listeners[event.data.queryMethodListener].apply(instance, event.data.queryMethodArguments); + } else { + this.defaultListener.call(instance, event.data); + } +}</pre> + +<p>现在回到worker中。首先我们需要一个能够完成这2个操作的方法:</p> + +<pre class="notranslate">var queryableFunctions = { + getDifference: function(a, b) { + reply('printStuff', a - b); + }, + waitSomeTime: function() { + setTimeout(function() { + reply('doAlert', 3, 'seconds'); + }, 3000); + } +} + +function reply() { + if (arguments.length < 1) { + throw new TypeError('reply - takes at least one argument'); + return; + } + postMessage({ + queryMethodListener: arguments[0], + queryMethodArguments: Array.prototype.slice.call(arguments, 1) + }); +} + +/* This method is called when main page calls QueryWorker's postMessage method directly*/ +function defaultReply(message) { + // do something +}</pre> + +<p><code>onmessage方法也就很简单了:</code></p> + +<pre class="notranslate">onmessage = function(event) { + if (event.data instanceof Object && + event.data.hasOwnProperty('queryMethod') && + event.data.hasOwnProperty('queryMethodArguments')) { + queryableFunctions[event.data.queryMethod] + .apply(self, event.data.queryMethodArguments); + } else { + defaultReply(event.data); + } +}</pre> + +<p>接下来给出一个完整的实现:</p> + +<p><strong>example.html</strong> (the main page):</p> + +<pre class="brush: html notranslate"><!doctype html> +<html> +<head> +<meta charset="UTF-8" /> +<title>MDN Example - Queryable worker</title> +<script type="text/javascript"> + /* + QueryableWorker instances methods: + * sendQuery(queryable function name, argument to pass 1, argument to pass 2, etc. etc): calls a Worker's queryable function + * postMessage(string or JSON Data): see Worker.prototype.postMessage() + * terminate(): terminates the Worker + * addListener(name, function): adds a listener + * removeListener(name): removes a listener + QueryableWorker instances properties: + * defaultListener: the default listener executed only when the Worker calls the postMessage() function directly + */ + function QueryableWorker (sURL, fDefListener, fOnError) { + var oInstance = this, oWorker = new Worker(sURL), oListeners = {}; + this.defaultListener = fDefListener || function () {}; + oWorker.onmessage = function (oEvent) { + if (oEvent.data instanceof Object && oEvent.data.hasOwnProperty("vo42t30") && oEvent.data.hasOwnProperty("rnb93qh")) { + oListeners[oEvent.data.vo42t30].apply(oInstance, oEvent.data.rnb93qh); + } else { + this.defaultListener.call(oInstance, oEvent.data); + } + }; + if (fOnError) { oWorker.onerror = fOnError; } + this.sendQuery = function (/* queryable function name, argument to pass 1, argument to pass 2, etc. etc */) { + if (arguments.length < 1) { throw new TypeError("QueryableWorker.sendQuery - not enough arguments"); return; } + oWorker.postMessage({ "bk4e1h0": arguments[0], "ktp3fm1": Array.prototype.slice.call(arguments, 1) }); + }; + this.postMessage = function (vMsg) { + //I just think there is no need to use call() method + //how about just oWorker.postMessage(vMsg); + //the same situation with terminate + //well,just a little faster,no search up the prototye chain + Worker.prototype.postMessage.call(oWorker, vMsg); + }; + this.terminate = function () { + Worker.prototype.terminate.call(oWorker); + }; + this.addListener = function (sName, fListener) { + oListeners[sName] = fListener; + }; + this.removeListener = function (sName) { + delete oListeners[sName]; + }; + }; + + // your custom "queryable" worker + var oMyTask = new QueryableWorker("my_task.js" /* , yourDefaultMessageListenerHere [optional], yourErrorListenerHere [optional] */); + + // your custom "listeners" + + oMyTask.addListener("printSomething", function (nResult) { + document.getElementById("firstLink").parentNode.appendChild(document.createTextNode(" The difference is " + nResult + "!")); + }); + + oMyTask.addListener("alertSomething", function (nDeltaT, sUnit) { + alert("Worker waited for " + nDeltaT + " " + sUnit + " :-)"); + }); +</script> +</head> +<body> + <ul> + <li><a id="firstLink" href="javascript:oMyTask.sendQuery('getDifference', 5, 3);">What is the difference between 5 and 3?</a></li> + <li><a href="javascript:oMyTask.sendQuery('waitSomething');">Wait 3 seconds</a></li> + <li><a href="javascript:oMyTask.terminate();">terminate() the Worker</a></li> + </ul> +</body> +</html></pre> + +<p><strong>my_task.js</strong> (the worker):</p> + +<pre class="brush: js notranslate">// your custom PRIVATE functions + +function myPrivateFunc1 () { + // do something +} + +function myPrivateFunc2 () { + // do something +} + +// etc. etc. + +// your custom PUBLIC functions (i.e. queryable from the main page) + +var queryableFunctions = { + // example #1: get the difference between two numbers: + getDifference: function (nMinuend, nSubtrahend) { + reply("printSomething", nMinuend - nSubtrahend); + }, + // example #2: wait three seconds + waitSomething: function () { + setTimeout(function() { reply("alertSomething", 3, "seconds"); }, 3000); + } +}; + +// system functions + +function defaultQuery (vMsg) { + // your default PUBLIC function executed only when main page calls the queryableWorker.postMessage() method directly + // do something +} + +function reply (/* listener name, argument to pass 1, argument to pass 2, etc. etc */) { + if (arguments.length < 1) { throw new TypeError("reply - not enough arguments"); return; } + postMessage({ "vo42t30": arguments[0], "rnb93qh": Array.prototype.slice.call(arguments, 1) }); +} + +onmessage = function (oEvent) { + if (oEvent.data instanceof Object && oEvent.data.hasOwnProperty("bk4e1h0") && oEvent.data.hasOwnProperty("ktp3fm1")) { + queryableFunctions[oEvent.data.bk4e1h0].apply(self, oEvent.data.ktp3fm1); + } else { + defaultQuery(oEvent.data); + } +};</pre> + +<p>这个实例中,可以对从主页面到worker、以及worker到主页面之间传递的消息内容进行切换。而且属性名"queryMethod", "queryMethodListeners","queryMethodArguments"可以是任何东西,只要它们在<code>QueryableWorker和worker</code>中保持一致。</p> + +<h3 id="通过转让所有权可转让对象来传递数据">通过转让所有权(可转让对象)来传递数据</h3> + +<p>Google Chrome 17 与 Firefox 18 包含另一种性能更高的方法来将特定类型的对象(<a href="http://w3c.github.io/html/infrastructure.html#transferable-objects">可转让对象</a>) 传递给一个 worker/从 worker 传回 。可转让对象从一个上下文转移到另一个上下文而不会经过任何拷贝操作。这意味着当传递大数据时会获得极大的性能提升。如果你从 C/C++ 世界来,那么把它想象成按照引用传递。然而与按照引用传递不同的是,一旦对象转让,那么它在原来上下文的那个版本将不复存在。该对象的所有权被转让到新的上下文内。例如,当你将一个 <a href="/en/JavaScript_typed_arrays/ArrayBuffer">ArrayBuffer </a>对象从主应用转让到 Worker 中,原始的 <code>ArrayBuffer</code> 被清除并且无法使用。它包含的内容会(完整无差的)传递给 Worker 上下文。</p> + +<pre class="brush: js notranslate">// Create a 32MB "file" and fill it. +var uInt8Array = new Uint8Array(1024*1024*32); // 32MB +for (var i = 0; i < uInt8Array .length; ++i) { + uInt8Array[i] = i; +} + +worker.postMessage(uInt8Array.buffer, [uInt8Array.buffer]); +</pre> + +<div class="note"> +<p><strong>注意:</strong>获取更多该方法相关的可转让对象、性能及特性检测等方法,请参阅HTML5 Rocks中的<a href="http://updates.html5rocks.com/2011/12/Transferable-Objects-Lightning-Fast">Transferable Objects: Lightning Fast!</a> 。</p> +</div> + +<h2 id="嵌入式_worker">嵌入式 worker</h2> + +<p>目前没有一种「官方」的方法能够像 {{ HTMLElement("script") }} 元素一样将 worker 的代码嵌入的网页中。但是如果一个 {{ HTMLElement("script") }} 元素没有 <code>src 特性,并且它的</code> <code>type</code> 特性没有指定成一个可运行的 mime-type,那么它就会被认为是一个数据块元素,并且能够被 JavaScript 使用。「数据块」是 HTML5 中一个十分常见的特性,它可以携带几乎任何文本类型的数据。所以,你能够以如下方式嵌入一个 worker:</p> + +<pre class="brush: html notranslate"><!DOCTYPE html> +<html> +<head> +<meta charset="UTF-8" /> +<title>MDN Example - Embedded worker</title> +<script type="text/js-worker"> + // 该脚本不会被 JS 引擎解析,因为它的 mime-type 是 text/js-worker。 + var myVar = "Hello World!"; + // 剩下的 worker 代码写到这里。 +</script> +<script type="text/javascript"> + // 该脚本会被 JS 引擎解析,因为它的 mime-type 是 text/javascript。 + function pageLog (sMsg) { + // 使用 fragment:这样浏览器只会进行一次渲染/重排。 + var oFragm = document.createDocumentFragment(); + oFragm.appendChild(document.createTextNode(sMsg)); + oFragm.appendChild(document.createElement("br")); + document.querySelector("#logDisplay").appendChild(oFragm); + } +</script> +<script type="text/js-worker"> + // 该脚本不会被 JS 引擎解析,因为它的 mime-type 是 text/js-worker。 + onmessage = function (oEvent) { + postMessage(myVar); + }; + // 剩下的 worker 代码写到这里。 +</script> +<script type="text/javascript"> + // 该脚本会被 JS 引擎解析,因为它的 mime-type 是 text/javascript。 + + // 在过去...: + // 我们使用 blob builder + // ...但是现在我们使用 Blob...: + var blob = new Blob(Array.prototype.map.call(document.querySelectorAll("script[type=\"text\/js-worker\"]"), function (oScript) { return oScript.textContent; }),{type: "text/javascript"}); + + // 创建一个新的 document.worker 属性,包含所有 "text/js-worker" 脚本。 + document.worker = new Worker(window.URL.createObjectURL(blob)); + + document.worker.onmessage = function (oEvent) { + pageLog("Received: " + oEvent.data); + }; + + // 启动 worker. + window.onload = function() { document.worker.postMessage(""); }; +</script> +</head> +<body><div id="logDisplay"></div></body> +</html></pre> + +<p>现在,嵌入式 worker 已经嵌套进了一个自定义的 <code>document.worker</code> 属性中。</p> + +<p>这样也不足为奇,你仍然可以将一个函数转换为blob,然后为这个blob生成URL对象。比如:</p> + +<pre class="notranslate">function fn2workerURL(fn) { + var blob = new Blob(['('+fn.toString()+')()'], {type: 'application/javascript'}) + return URL.createObjectURL(blob) +} +</pre> + +<h2 id="更多示例">更多示例</h2> + +<p>本节提供了几个如何使用 DOM worker 的例子。</p> + +<h3 id="在后台执行运算">在后台执行运算</h3> + +<p>worker 的一个优势在于能够执行处理器密集型的运算而不会阻塞 UI 线程。在下面的例子中,worker 用于计算斐波那契数。</p> + +<h4 id="JavaScript_代码">JavaScript 代码</h4> + +<p>下面的 JavaScript 代码保存在「fibonacci.js」文件中,与下一节的 HTML 文件关联。</p> + +<pre class="brush: js notranslate">var results = []; + +function resultReceiver(event) { + results.push(parseInt(event.data)); + if (results.length == 2) { + postMessage(results[0] + results[1]); + } +} + +function errorReceiver(event) { + throw event.data; +} + +onmessage = function(event) { + var n = parseInt(event.data); + + if (n == 0 || n == 1) { + postMessage(n); + return; + } + + for (var i = 1; i <= 2; i++) { + var worker = new Worker("fibonacci.js"); + worker.onmessage = resultReceiver; + worker.onerror = errorReceiver; + worker.postMessage(n - i); + } + };</pre> + +<p>worker 将属性 <code>onmessage</code> 设置为一个函数,当 worker 对象调用 <code>postMessage() 时该函数会接收到发送过来的信息。</code>(注意,这么使用并不等同于定义一个同名的全局<em>变量</em> ,或是定义一个同名的<em>函数</em>。<code>var onmessage</code> 与 <code>function onmessage</code> 将会定义与该名字相同的全局属性,但是它们不会注册能够接收从创建 worker 的网页发送过来的消息的函数。) 这会启用递归,生成自己的新拷贝来处理计算的每一个循环。</p> + +<h4 id="HTML_代码">HTML 代码</h4> + +<pre class="brush: html notranslate"><!DOCTYPE html> +<html> + <head> + <meta charset="UTF-8" /> + <title>Test threads fibonacci</title> + </head> + <body> + + <div id="result"></div> + + <script language="javascript"> + + var worker = new Worker("fibonacci.js"); + + worker.onmessage = function(event) { + document.getElementById("result").textContent = event.data; + dump("Got: " + event.data + "\n"); + }; + + worker.onerror = function(error) { + dump("Worker error: " + error.message + "\n"); + throw error; + }; + + worker.postMessage("5"); + + </script> + </body> +</html> +</pre> + +<p>网页创建了一个 <code>div</code> 元素,ID 为 <code>result</code> , 用它来显示运算结果,然后生成 worker。在生成 worker 后,<code>onmessage</code> 处理函数配置为通过设置 <code>div</code> 元素的内容来显示运算结果,然后 <code>onerror</code> 处理函数被设置为 <a class="external" href="/en/Debugging_JavaScript#dump()">转储</a> 错误信息。</p> + +<p>最后,向 worker 发送一条信息来启动它。</p> + +<p><a class="external" href="/samples/workers/fibonacci">运行这个例子</a>。</p> + +<h3 id="在后台运行_web_IO">在后台运行 web I/O</h3> + +<p>你可以在 <a class="internal" href="/En/Using_workers_in_extensions">在扩展中使用 worker</a> 这篇文章中找到相关例子。</p> + +<h3 id="划分任务给多个_worker">划分任务给多个 worker</h3> + +<p>当多核系统流行开来,将复杂的运算任务分配给多个 worker 来运行已经变得十分有用,这些 worker 会在多处理器内核上运行这些任务。</p> + +<h2 id="其它类型的worker">其它类型的worker</h2> + +<p>除了专用和共享的web worker,还有一些其它类型的worker:</p> + +<ul> + <li><a href="https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorker_API">ServiceWorkers</a> (服务worker)一般作为web应用程序、浏览器和网络(如果可用)之前的代理服务器。它们旨在(除开其他方面)创建有效的离线体验,拦截网络请求,以及根据网络是否可用采取合适的行动并更新驻留在服务器上的资源。他们还将允许访问推送通知和后台同步API。</li> + <li>Chrome Workers 是一种仅适用于firefox的worker。如果您正在开发附加组件,希望在扩展程序中使用worker且有在你的worker中访问 <a href="https://developer.mozilla.org/en/js-ctypes">js-ctypes</a> 的权限,你可以使用Chrome Workers。详情请参阅{{domxref("ChromeWorker")}}。</li> + <li><a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API#Audio_Workers">Audio Workers</a> (音频worker)使得在web worker上下文中直接完成脚本化音频处理成为可能。</li> +</ul> + +<h2 id="worker中可用的函数和接口">worker中可用的函数和接口</h2> + +<p>你可以在web worker中使用大多数的标准javascript特性,包括</p> + +<ul> + <li>{{domxref("Navigator")}}</li> + <li>{{domxref("XMLHttpRequest")}}</li> + <li>{{jsxref("Global_Objects/Array", "Array")}}, {{jsxref("Global_Objects/Date", "Date")}}, {{jsxref("Global_Objects/Math", "Math")}}, and {{jsxref("Global_Objects/String", "String")}}</li> + <li>{{domxref("WindowTimers.setTimeout")}} and {{domxref("WindowTimers.setInterval")}}</li> +</ul> + +<p>在一个worker中最主要的你不能做的事情就是直接影响父页面。包括操作父页面的节点以及使用页面中的对象。你只能间接地实现,通过{{domxref("DedicatedWorkerGlobalScope.postMessage")}}回传消息给主脚本,然后从主脚本那里执行操作或变化。</p> + +<div class="note"> +<p><strong>注意:</strong>获取worker中完整的方法列表,请参阅<a href="https://developer.mozilla.org/en-US/docs/Web/Reference/Functions_and_classes_available_to_workers">Functions and interfaces available to workers</a>。</p> +</div> + +<h2 id="规范">规范</h2> + +<table class="standard-table"> + <tbody> + <tr> + <th scope="col">Specification</th> + <th scope="col">Status</th> + <th scope="col">Comment</th> + </tr> + <tr> + <td>{{SpecName('HTML WHATWG', '#workers', 'Web workers')}}</td> + <td>{{Spec2('HTML WHATWG')}}</td> + <td></td> + </tr> + </tbody> +</table> + +<h2 id="相关链接">相关链接</h2> + +<ul> + <li><code><a href="https://developer.mozilla.org/en-US/docs/Web/API/Worker">Worker</a></code> 接口</li> + <li><code><a href="https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker">SharedWorker</a></code> 接口</li> + <li><a href="https://developer.mozilla.org/en-US/docs/Web/API/Worker/Functions_and_classes_available_to_workers">worker提供的方法</a></li> +</ul> |
