65.9K
CodeProject 正在变化。 阅读更多。
Home

JavaScript 异步编程(太棒了)。

starIconstarIconstarIconstarIconstarIcon

5.00/5 (20投票s)

2015年11月4日

CPOL

5分钟阅读

viewsIcon

47210

Microsoft 项目经理 David Cathuhe 分享了关于 ECMAScript 中异步代码的概述:它是什么,如何工作,以及它如何改进你在 JavaScript 工作流。

JavaScript 自早期版本以来已经取得了长足的进步,这要归功于 TC39(负责标准化 JavaScript(或更准确地说是 ECMAScript)的组织)所做的所有努力,我们现在拥有了一种被广泛使用的现代语言。

ECMAScript 中获得巨大改进的一个领域是异步代码。如果你是新手开发者,你可以在 这里 了解更多关于异步编程的知识。幸运的是,我们在 Windows 10 的新 Edge 浏览器中包含了这些更改——请查看 Microsoft Edge 更新日志

在所有这些新功能中,让我们特别关注“ES2016 Async Functions”(在实验性 JavaScript 功能标志后面),并一起探索更新,看看 ECMAScript 如何改进你当前的工作流程。

第一站:ECMAScript 5 – 回调城市

ECMAScript 5(以及之前的版本)都围绕着回调。为了更好地说明这一点,让我们举一个你每天肯定会使用不止一次的简单例子:执行 XHR 请求。

var displayDiv = document.getElementById("displayDiv");

// Part 1 - Defining what do we want to do with the result
var processJSON = function (json) {
    var result = JSON.parse(json);

    result.collection.forEach(function(card) {
        var div = document.createElement("div");
        div.innerHTML = card.name + " cost is " + card.price;

        displayDiv.appendChild(div);
    });
}

// Part 2 - Providing a function to display errors
var displayError = function(error) {
    displayDiv.innerHTML = error;
}

// Part 3 - Creating and setting up the XHR object
var xhr = new XMLHttpRequest();

xhr.open('GET', "cards.json");

// Part 4 - Defining callbacks that XHR object will call for us
xhr.onload = function(){
    if (xhr.status === 200) {
        processJSON(xhr.response);
    }
}

xhr.onerror = function() {
    displayError("Unable to load RSS");
}

// Part 5 - Starting the process
xhr.send();

经验丰富的 JavaScript 开发人员会注意到这看起来多么熟悉,因为 XHR 回调一直都在使用!它很简单,而且相当直接:开发人员创建一个 XHR 请求,然后为指定的 XHR 对象提供回调。

相比之下,由于异步代码的内在特性,回调的复杂性来自于其非线性的执行顺序。

当在自己的回调中使用另一个异步调用时,“回调地狱”会变得更加糟糕。

第二站:ECMAScript 6 – Promise 城市


ECMAScript 6 正在获得势头,Edge 在支持方面处于领先地位,迄今为止覆盖率达到 88%。

在许多重大改进中,ECMAScript 6 标准化了Promise(以前称为 future)的使用。

根据 MDN 的说法,Promise 是一个用于延迟和异步计算的对象。Promise 代表一个尚未完成但预期在未来完成的操作。Promise 是一种组织异步操作的方式,使其看起来像同步操作。这正是我们 XHR 示例所需要的。

Promise 已经存在一段时间了,但好消息是现在你不再需要任何库,因为浏览器已经提供了它们。

让我们稍微更新一下我们的示例以支持Promise,看看它如何改进我们代码的可读性和可维护性。

var displayDiv = document.getElementById("displayDiv");

// Part 1 - Create a function that returns a promise
function getJsonAsync(url) {
    // Promises require two functions: one for success, one for failure
    return new Promise(function (resolve, reject) {
        var xhr = new XMLHttpRequest();

        xhr.open('GET', url);

        xhr.onload = () => {
            if (xhr.status === 200) {
                // We can resolve the promise
                resolve(xhr.response);
            } else {
                // It's a failure, so let's reject the promise
                reject("Unable to load RSS");
            }
        }

        xhr.onerror = () => {
            // It's a failure, so let's reject the promise
            reject("Unable to load RSS");
        };

        xhr.send();
    });
}

// Part 2 - The function returns a promise
// so we can chain with a .then and a .catch
getJsonAsync("cards.json").then(json => {
    var result = JSON.parse(json);

    result.collection.forEach(card => {
        var div = document.createElement("div");
        div.innerHTML = `${card.name} cost is ${card.price}`;

        displayDiv.appendChild(div);
    });
}).catch(error => {
    displayDiv.innerHTML = error;
});

你可能已经注意到了很多改进。让我们仔细看看。

创建 Promise

为了“Promise 化”(抱歉,我是法国人,所以我可以发明新词)旧的 XHR 对象,你需要创建一个 Promise 对象。

使用 Promise

创建后,Promise 可以用于以更优雅的方式链接异步调用。

所以现在(从用户的角度来看)我们有:

  • 获取 Promise (1)
  • 链接成功代码 (2 和 3)
  • 链接错误代码 (4),就像在 try/catch 块中一样。

有趣的是,链接Promise 可以很容易地通过.then().then() 等方式调用。

旁注:由于 JavaScript 是一门现代语言,你可能会注意到我还使用了 ECMAScript 6语法糖,如模板字符串箭头函数

终点:ECMAScript 7 – 异步城市

终于,我们到达了目的地!我们几乎已经到达未来,但得益于 Edge 快速的开发周期,团队能够在最新版本中引入一点ECMAScript 7async functions

Async functions 是一种语法糖,用于改进编写异步代码的语言级模型。

Async functions 构建在 ECMAScript 6 功能(如生成器)之上。事实上,生成器可以与 Promise 结合使用以产生相同的结果,但需要更多的用户代码。

我们不需要更改生成 Promise 的函数,因为 async functions 可以直接与 Promise 一起工作。

我们只需要更改调用函数。

// Let's create an async anonymous function
(async function() {
    try {
        // Just have to await the promise!
        var json = await getJsonAsync("cards.json");
        var result = JSON.parse(json);

        result.collection.forEach(card => {
            var div = document.createElement("div");
            div.innerHTML = `${card.name} cost is ${card.price}`;

            displayDiv.appendChild(div);
        });
    } catch (e) {
        displayDiv.innerHTML = e;
    }
})();

这就是魔法发生的地方。这段代码看起来像普通的同步代码,具有完美的线性执行路径。

相当令人印象深刻,对吧?

好消息是,你甚至可以将 async functions 与箭头函数或类方法一起使用。

深入研究

如果你想了解更多关于我们在 Chakra 中如何实现它的细节,请查看 Microsoft Edge 博客上的官方帖子。你还可以使用 Kangax 的网站跟踪各种浏览器实现ECMAScript 67 的进度。

也请随时查看我们的 JavaScript 路线图!请不要犹豫,通过使用投票按钮来提供你的反馈并支持你喜欢的特性。

感谢阅读,我们期待听到你的反馈和想法!

更多 Web 开发实践

本文是 Microsoft 技术布道师和工程师关于实用 JavaScript 学习、开源项目和互操作性最佳实践的 Web 开发系列的一部分,包括 Microsoft Edge 浏览器和新的 EdgeHTML 渲染引擎

我们鼓励你在包括 Microsoft Edge(Windows 10 的默认浏览器)在内的各种浏览器和设备上进行测试,可以使用 dev.modern.IE 上的免费工具。

深入了解 Microsoft Edge 和 Web 平台的技术学习

更多免费的跨平台工具和网络平台资源

© . All rights reserved.