选择正确的方法

翻译正在进行中。

为了完成这个模块,我们将简要讨论我们在整个过程中讨论的不同编码技术和功能,看看你应该使用哪一个,并提供适当的常见陷阱的建议和提醒。随着时间的推移,我们可能会添加到此资源中。

预备条件: 基本的计算机素养,对JavaScript基础知识的合理理解。
目标: 能够在使用不同的异步编程技术时做出合理的选择。

异步回调

通常在旧式API中找到,涉及将函数作为参数传递给另一个函数,然后在异步操作完成时调用该函数,以便回调可以依次对结果执行某些操作。这是promise的先导;它不那么高效或灵活。仅在必要时使用。

Useful for...
Single delayed operation Repeating operation Multiple sequential operations Multiple simultaneous operations
No Yes (recursive callbacks) Yes (nested callbacks) No

代码示例

通过XMLHttpRequest API加载资源的示例(run it live,并查看see the source):

function loadAsset(url, type, callback) {
  let xhr = new XMLHttpRequest();
  xhr.open('GET', url);
  xhr.responseType = type;

  xhr.onload = function() {
    callback(xhr.response);
  };

  xhr.send();
}

function displayImage(blob) {
  let objectURL = URL.createObjectURL(blob);

  let image = document.createElement('img');
  image.src = objectURL;
  document.body.appendChild(image);
}

loadAsset('coffee.jpg', 'blob', displayImage);

缺陷

  • 嵌套回调可能很麻烦且难以阅读(即“回调地狱”)
  • 每层嵌套都需要调用一次失败回调,而使用promises,您只需使用一个.catch()代码块来处理整个链的错误。
  • 异步回调不是很优雅。
  • Promise回调总是按照它们放在事件队列中的严格顺序调用;异步回调不是。

浏览器兼容性

非常好的一般支持,尽管API中回调的确切支持取决于特定的API。有关更具体的支持信息,请参阅您正在使用的API的参考文档。

更多信息

setTimeout()

setTimeout() 是一种允许您在经过任意时间后运行函数的方法

Useful for...
Single delayed operation Repeating operation Multiple sequential operations Multiple simultaneous operations
Yes Yes (recursive timeouts) Yes (nested timeouts) No

代码示例

这里浏览器将在执行匿名函数之前等待两秒钟,然后将显示警报消息(see it running livesee the source code):

let myGreeting = setTimeout(function() {
  alert('Hello, Mr. Universe!');
}, 2000)

缺陷

您可以使用递归的setTimeout()调用以类似于setInterval()的方式重复运行函数,使用如下代码:

let i = 1;
setTimeout(function run() {
  console.log(i);
  i++;

  setTimeout(run, 100);
}, 100);

递归setTimeout()setInterval()之间存在差异:

  • 递归setTimeout()保证执行之间至少经过指定的时间(在本例中为100ms);代码将运行,然后等待100毫秒再次运行。无论代码运行多长时间,间隔都是相同的。
  • 使用setInterval(),我们选择的间隔包括执行我们想要运行的代码所花费的时间。假设代码需要40毫秒才能运行 ––然后间隔最终只有60毫秒。

当你的代码有可能比你分配的时间间隔更长时间运行时,最好使用递归的setTimeout() ––这将使执行之间的时间间隔保持不变,无论代码执行多长时间,你不会得到错误。

浏览器兼容性

Update compatibility data on GitHub
DesktopMobile
ChromeEdgeFirefoxInternet ExplorerOperaSafariAndroid webviewChrome for AndroidFirefox for AndroidOpera for AndroidSafari on iOSSamsung Internet
setTimeoutChrome Full support 30Edge Full support YesFirefox Full support 1
Full support 1
Full support 52
Notes
Notes setInterval now defined on WindowOrWorkerGlobalScope mixin.
IE Full support 4Opera Full support 4Safari Full support 1WebView Android Full support YesChrome Android Full support 30Firefox Android Full support 4
Full support 4
Full support 52
Notes
Notes setInterval now defined on WindowOrWorkerGlobalScope mixin.
Opera Android Full support 10.1Safari iOS Full support 1Samsung Internet Android ?
Supports parameters for callbackChrome Full support YesEdge Full support YesFirefox Full support YesIE Full support 10Opera Full support YesSafari ? WebView Android Full support YesChrome Android Full support YesFirefox Android ? Opera Android ? Safari iOS ? Samsung Internet Android ?
Throttling of tracking timeout scriptsChrome ? Edge ? Firefox Full support 55IE ? Opera ? Safari ? WebView Android ? Chrome Android ? Firefox Android Full support 55Opera Android ? Safari iOS ? Samsung Internet Android ?

Legend

Full support  
Full support
Compatibility unknown  
Compatibility unknown
See implementation notes.
See implementation notes.

更多信息

setInterval()

setInterval()是一种允许您在每次执行之间以设定的时间间隔重复运行函数的方法。不如requestAnimationFrame()有效,但允许您选择运行速率/帧速率。

Useful for...
Single delayed operation Repeating operation Multiple sequential operations Multiple simultaneous operations
No Yes No (unless it is the same one) No

代码示例

以下函数创建一个新的Date()对象,使用toLocaleTimeString()从中提取时间字符串,然后在UI中显示它。然后我们使用setInterval()每秒运行一次,创建每秒更新一次的数字时钟的效果(see this livesee the source):

function displayTime() {
   let date = new Date();
   let time = date.toLocaleTimeString();
   document.getElementById('demo').textContent = time;
}

const createClock = setInterval(displayTime, 1000);

缺陷

  • 帧速率未针对运行动画的系统进行优化,并且可能效率低下。除非您需要选择特定(较慢)的帧速率,否则通常最好使用requestAnimationFrame().

浏览器兼容性

Update compatibility data on GitHub
DesktopMobile
ChromeEdgeFirefoxInternet ExplorerOperaSafariAndroid webviewChrome for AndroidFirefox for AndroidOpera for AndroidSafari on iOSSamsung Internet
setIntervalChrome Full support 30Edge Full support YesFirefox Full support 1
Full support 1
Full support 52
Notes
Notes setInterval now defined on WindowOrWorkerGlobalScope mixin.
IE Full support 4Opera Full support 4Safari Full support 1WebView Android Full support YesChrome Android Full support 30Firefox Android Full support 4
Full support 4
Full support 52
Notes
Notes setInterval now defined on WindowOrWorkerGlobalScope mixin.
Opera Android Full support 10.1Safari iOS Full support 1Samsung Internet Android ?
Supports parameters for callbackChrome Full support YesEdge Full support YesFirefox Full support YesIE Full support 10Opera Full support YesSafari ? WebView Android Full support YesChrome Android Full support YesFirefox Android ? Opera Android ? Safari iOS ? Samsung Internet Android ?

Legend

Full support  
Full support
Compatibility unknown  
Compatibility unknown
See implementation notes.
See implementation notes.

更多信息

requestAnimationFrame()

requestAnimationFrame()是一种允许您以给定当前浏览器/系统的最佳帧速率重复且高效地运行函数的方法。除非您需要特定的速率帧,否则您应该尽可能使用它而不要去使用setInterval()/recursive setTimeout()

Useful for...
Single delayed operation Repeating operation Multiple sequential operations Multiple simultaneous operations
No Yes No (unless it is the same one) No

代码示例

一个简单的动画旋转器;你可以查看example live on GitHub(参见 source code ):

const spinner = document.querySelector('div');
let rotateCount = 0;
let startTime = null;
let rAF;

function draw(timestamp) {
  if(!startTime) {
    startTime = timestamp;
  }

  let rotateCount = (timestamp - startTime) / 3;

  spinner.style.transform = 'rotate(' + rotateCount + 'deg)';

  if(rotateCount > 359) {
    rotateCount = 0;
  }
 
  rAF = requestAnimationFrame(draw);
}

draw();

缺陷

  • 您无法使用requestAnimationFrame()选择特定的帧速率。如果需要以较慢的帧速率运行动画,则需要使用setInterval()或递归的setTimeout()

浏览器兼容性

Update compatibility data on GitHub
DesktopMobile
ChromeEdgeFirefoxInternet ExplorerOperaSafariAndroid webviewChrome for AndroidFirefox for AndroidOpera for AndroidSafari on iOSSamsung Internet
requestAnimationFrameChrome Full support 24
Full support 24
Full support 10
Prefixed
Prefixed Implemented with the vendor prefix: webkit
Edge Full support 12Firefox Full support 23
Notes
Full support 23
Notes
Notes Callback parameter is a DOMHighResTimestamp. This means ten microsecond precision and zero time as performance.now().
No support 11 — 42
Prefixed Notes
Prefixed Implemented with the vendor prefix: moz
Notes Callback parameter is a DOMTimestamp. This means millisecond precision and zero time as Date.now().
No support 4 — 11
Prefixed Notes
Prefixed Implemented with the vendor prefix: moz
Notes Could be called with no input parameters.
IE Full support 10Opera Full support 15
Full support 15
No support ? — 15
Prefixed
Prefixed Implemented with the vendor prefix: o
Safari Full support 6.1
Full support 6.1
Full support 6
Prefixed
Prefixed Implemented with the vendor prefix: webkit
WebView Android Full support YesChrome Android Full support 25
Full support 25
Full support 18
Prefixed
Prefixed Implemented with the vendor prefix: webkit
Firefox Android Full support 23
Full support 23
No support 14 — 42
Prefixed
Prefixed Implemented with the vendor prefix: moz
Opera Android Full support 14
Full support 14
No support ? — 14
Prefixed
Prefixed Implemented with the vendor prefix: o
Safari iOS Full support 7.1
Full support 7.1
Full support 6.1
Prefixed
Prefixed Implemented with the vendor prefix: webkit
Samsung Internet Android Full support Yes
Return valueChrome Full support 23Edge Full support YesFirefox Full support 11IE Full support 10Opera Full support 15Safari Full support 6.1WebView Android Full support YesChrome Android Full support 25Firefox Android Full support 14Opera Android Full support 14Safari iOS Full support 6.1Samsung Internet Android ?

Legend

Full support  
Full support
Compatibility unknown  
Compatibility unknown
See implementation notes.
See implementation notes.
Requires a vendor prefix or different name for use.
Requires a vendor prefix or different name for use.

更多信息

Promises

Promises 是一种JavaScript功能,允许您运行异步操作并等到它完全完成后再根据其结果运行另一个操作。 Promise是现代异步JavaScript的支柱。

Useful for...
Single delayed operation Repeating operation Multiple sequential operations Multiple simultaneous operations
No No Yes See Promise.all(), below

代码示例

以下代码从服务器获取图像并将其显示在 <img> 元素中;(see it live alsothe source code):

fetch('coffee.jpg')
.then(response => response.blob())
.then(myBlob => {
  let objectURL = URL.createObjectURL(myBlob);
  let image = document.createElement('img');
  image.src = objectURL;
  document.body.appendChild(image);
})
.catch(e => {
  console.log('There has been a problem with your fetch operation: ' + e.message);
});

缺陷

Promise链可能很复杂,难以解析。如果你嵌套了许多promises,你最终可能会遇到类似的麻烦来回调地狱。例如:

remotedb.allDocs({
  include_docs: true,
  attachments: true
}).then(function (result) {
  var docs = result.rows;
  docs.forEach(function(element) {
    localdb.put(element.doc).then(function(response) {
      alert("Pulled doc with id " + element.doc._id + " and added to local db.");
    }).catch(function (err) {
      if (err.name == 'conflict') {
        localdb.get(element.doc._id).then(function (resp) {
          localdb.remove(resp._id, resp._rev).then(function (resp) {
// et cetera...

最好使用promises的链功能,这样使用更平顺,更易于解析的结构:

remotedb.allDocs(...).then(function (resultOfAllDocs) {
  return localdb.put(...);
}).then(function (resultOfPut) {
  return localdb.get(...);
}).then(function (resultOfGet) {
  return localdb.put(...);
}).catch(function (err) {
  console.log(err);
});

乃至:

remotedb.allDocs(...)
.then(resultOfAllDocs => {
  return localdb.put(...);
})
.then(resultOfPut => {
  return localdb.get(...);
})
.then(resultOfGet => {
  return localdb.put(...);
})
.catch(err => console.log(err));

这涵盖了很多基础知识。对于更完整的治疗,请参阅诺兰劳森的We have a problem with promises

浏览器兼容性

Update compatibility data on GitHub
DesktopMobileServer
ChromeEdgeFirefoxInternet ExplorerOperaSafariAndroid webviewChrome for AndroidFirefox for AndroidOpera for AndroidSafari on iOSSamsung InternetNode.js
PromiseChrome Full support 32Edge Full support YesFirefox Full support 29IE No support NoOpera Full support 19Safari Full support 8WebView Android Full support 4.4.3Chrome Android Full support 32Firefox Android Full support 29Opera Android Full support YesSafari iOS Full support 8Samsung Internet Android Full support Yesnodejs Full support 0.12
Promise() constructorChrome Full support 32Edge Full support YesFirefox Full support 29
Notes
Full support 29
Notes
Notes Constructor requires a new operator since version 37.
IE No support NoOpera Full support 19Safari Full support 8
Notes
Full support 8
Notes
Notes Constructor requires a new operator since version 10.
WebView Android Full support 4.4.3Chrome Android Full support 32Firefox Android Full support 29
Notes
Full support 29
Notes
Notes Constructor requires a new operator since version 37.
Opera Android Full support YesSafari iOS Full support 8
Notes
Full support 8
Notes
Notes Constructor requires a new operator since version 10.
Samsung Internet Android Full support Yesnodejs Full support 0.12
Notes
Full support 0.12
Notes
Notes Constructor requires a new operator since version 4.
all()Chrome Full support 32Edge Full support 12Firefox Full support 29IE No support NoOpera Full support 19Safari Full support 8WebView Android Full support 4.4.3Chrome Android Full support 32Firefox Android Full support 29Opera Android Full support YesSafari iOS Full support 8Samsung Internet Android Full support Yesnodejs Full support 0.12
allSettled()Chrome Full support 76Edge No support NoFirefox No support NoIE No support NoOpera ? Safari ? WebView Android Full support 76Chrome Android Full support 76Firefox Android No support NoOpera Android Full support YesSafari iOS ? Samsung Internet Android ? nodejs ?
prototypeChrome Full support 32Edge Full support YesFirefox Full support 29IE No support NoOpera Full support 19Safari Full support 8WebView Android Full support 4.4.3Chrome Android Full support 32Firefox Android Full support 29Opera Android Full support YesSafari iOS Full support 8Samsung Internet Android Full support Yesnodejs Full support 0.12
catch()Chrome Full support 32Edge Full support 12Firefox Full support 29IE No support NoOpera Full support 19Safari Full support 8WebView Android Full support 4.4.3Chrome Android Full support 32Firefox Android Full support 29Opera Android Full support YesSafari iOS Full support 8Samsung Internet Android Full support Yesnodejs Full support 0.12
finally()Chrome Full support 63Edge Full support 18Firefox Full support 58IE No support NoOpera Full support 50Safari Full support 11.1WebView Android Full support 63Chrome Android Full support 63Firefox Android Full support 58Opera Android Full support 46Safari iOS Full support 11.1Samsung Internet Android No support Nonodejs Full support 10.0.0
then()Chrome Full support 32Edge Full support 12Firefox Full support 29IE No support NoOpera Full support 19Safari Full support 8WebView Android Full support 4.4.3Chrome Android Full support 32Firefox Android Full support 29Opera Android Full support YesSafari iOS Full support 8Samsung Internet Android Full support Yesnodejs Full support 0.12
race()Chrome Full support 32Edge Full support 12Firefox Full support 29IE No support NoOpera Full support 19Safari Full support 8WebView Android Full support 4.4.3Chrome Android Full support 32Firefox Android Full support 29Opera Android Full support YesSafari iOS Full support 8Samsung Internet Android Full support Yesnodejs Full support 0.12
reject()Chrome Full support 32Edge Full support 12Firefox Full support 29IE No support NoOpera Full support 19Safari Full support 8WebView Android Full support 4.4.3Chrome Android Full support 32Firefox Android Full support 29Opera Android Full support YesSafari iOS Full support 8Samsung Internet Android Full support Yesnodejs Full support 0.12
resolve()Chrome Full support 32Edge Full support 12Firefox Full support 29IE No support NoOpera Full support 19Safari Full support 8WebView Android Full support 4.4.3Chrome Android Full support 32Firefox Android Full support 29Opera Android Full support YesSafari iOS Full support 8Samsung Internet Android Full support Yesnodejs Full support 0.12

Legend

Full support  
Full support
No support  
No support
Compatibility unknown  
Compatibility unknown
See implementation notes.
See implementation notes.

更多信息

Promise.all()

一种JavaScript功能,允许您等待多个promises完成,然后根据所有其他promises的结果运行进一步的操作。

Useful for...
Single delayed operation Repeating operation Multiple sequential operations Multiple simultaneous operations
No No No Yes

代码示例

以下示例从服务器获取多个资源,并使用Promise.all()等待所有资源可用,然后显示所有这些资源––  see it live,并查看source code

function fetchAndDecode(url, type) {
  // Returning the top level promise, so the result of the entire chain is returned out of the function
  return fetch(url).then(response => {
    // Depending on what type of file is being fetched, use the relevant function to decode its contents
    if(type === 'blob') {
      return response.blob();
    } else if(type === 'text') {
      return response.text();
    }
  })
  .catch(e => {
    console.log(`There has been a problem with your fetch operation for resource "${url}": ` + e.message);
  });
}

// Call the fetchAndDecode() method to fetch the images and the text, and store their promises in variables
let coffee = fetchAndDecode('coffee.jpg', 'blob');
let tea = fetchAndDecode('tea.jpg', 'blob');
let description = fetchAndDecode('description.txt', 'text');

// Use Promise.all() to run code only when all three function calls have resolved
Promise.all([coffee, tea, description]).then(values => {
  console.log(values);
  // Store each value returned from the promises in separate variables; create object URLs from the blobs
  let objectURL1 = URL.createObjectURL(values[0]);
  let objectURL2 = URL.createObjectURL(values[1]);
  let descText = values[2];

  // Display the images in <img> elements
  let image1 = document.createElement('img');
  let image2 = document.createElement('img');
  image1.src = objectURL1;
  image2.src = objectURL2;
  document.body.appendChild(image1);
  document.body.appendChild(image2);

  // Display the text in a paragraph
  let para = document.createElement('p');
  para.textContent = descText;
  document.body.appendChild(para);
});

缺陷

  • 如果Promise.all()拒绝,那么你在其数组参数中输入的一个或多个promise(s)必须拒绝,或者可能根本不返回promises。你需要检查每一个,看看他们返回了什么。

浏览器兼容性

Update compatibility data on GitHub
DesktopMobileServer
ChromeEdgeFirefoxInternet ExplorerOperaSafariAndroid webviewChrome for AndroidFirefox for AndroidOpera for AndroidSafari on iOSSamsung InternetNode.js
all()Chrome Full support 32Edge Full support 12Firefox Full support 29IE No support NoOpera Full support 19Safari Full support 8WebView Android Full support 4.4.3Chrome Android Full support 32Firefox Android Full support 29Opera Android Full support YesSafari iOS Full support 8Samsung Internet Android Full support Yesnodejs Full support 0.12

Legend

Full support  
Full support
No support  
No support

更多信息

Async/await

构造在promises之上的语法的甜头,允许您使用更像编写同步回调代码的语法来运行异步操作。

Useful for...
Single delayed operation Repeating operation Multiple sequential operations Multiple simultaneous operations
No No Yes Yes (in combination with Promise.all())

代码示例

以下示例是我们之前看到的简单承诺示例的重构,该示例获取并显示图像,使用async / await编写(see it live,并查看source code):

async function myFetch() {
  let response = await fetch('coffee.jpg');
  let myBlob = await response.blob();

  let objectURL = URL.createObjectURL(myBlob);
  let image = document.createElement('img');
  image.src = objectURL;
  document.body.appendChild(image);
}

myFetch();

缺陷

  • 您不能在非async函数内或代码的顶级上下文环境中使用await运算符。这有时会导致需要创建额外的函数封包,这在某些情况下会略微令人沮丧。但大部分时间都值得。
  • 浏览器对async / await的支持不如promises那样好。如果你想使用async / await但是担心旧的浏览器支持,你可以考虑使用BabelJS 库 - 这允许你使用最新的JavaScript编写应用程序,让Babel找出用户浏览器需要的更改。

浏览器兼容性

Update compatibility data on GitHub
DesktopMobileServer
ChromeEdgeFirefoxInternet ExplorerOperaSafariAndroid webviewChrome for AndroidFirefox for AndroidOpera for AndroidSafari on iOSSamsung InternetNode.js
async functionChrome Full support 55Edge Full support YesFirefox Full support 52IE No support NoOpera Full support 42Safari Full support 10.1WebView Android Full support YesChrome Android Full support 55Firefox Android Full support 52Opera Android Full support 42Safari iOS Full support 10.1Samsung Internet Android Full support 6.0nodejs Full support 7.6.0
Full support 7.6.0
Full support 7.0.0
Disabled
Disabled From version 7.0.0: this feature is behind the --harmony runtime flag.

Legend

Full support  
Full support
No support  
No support
User must explicitly enable this feature.
User must explicitly enable this feature.

更多信息

本章内容

文档标签和贡献者

此页面的贡献者: PYGC, wangfangping, tjls
最后编辑者: PYGC,