发布于 5年前

异步JavaScript基础了解

从一开始,JavaScript似乎是一种简单的语言。在不深入研究主题的情况下看起来似乎也是如此,实际上,JS远非最容易学习的编程语言。只有全面深入才能发现JS的复杂性。要理解的方面之一是异步编程。虽然许多程序员已经在他们的代码中使用了它,但可能甚至不知道它是如何工作的。在本文中,我将尝试以最简单的方式解释这个概念,让我们开始吧。?

在MacBook Pro前面两人握手


事件循环

首先,异步意味着什么。我们可以说这意味着某些东西不是同步的(以预定义的顺序运行)但我认为我们可以做得更好。

为了更好地理解它,我们必须了解一些基础知识。JavaScript是一种单线程语言。您的代码仅在一个线程上运行,因此它不是并且不能并发(通常是异步错误解释)。

此外,JS代码是逐段线性地执行的,每个程序通常由至少2个片段(调用,函数,操作等)组成。代码中发生的每个事件(事件意味着将运行的代码)由所谓的事件循环处理。每次发生此循环的迭代时,下一个事件运行。这就是JS的工作原理。使用这段代码可以更好地理解这个概念:

const eventLoop = [/*events*/];
let event;
while(true){
    event = eventLoop.shift();
    event();
}

这段非常简单的代码基本上说明了这个永无止境的事件循环如何运行你的JS代码。

为了更好地理解这一点,让我们将这个过程与人类思维进行比较。你可能会说我们的大脑允许我们一次做很多事情,即所谓的多任务处理。但我认为我们的大脑更像是单线程超级计算机,每个任务都是逐个完成的,或者将它们分成更小的部分。这就像超快速异步事件循环。你首先做一件事然后做另一件事。如果某些东西可能会花费你更多的时间/精力,你可以把它分成更小的块(类似于要执行的任务列表)并按特定顺序执行它们,同时,在这些小任务之间,您执行完全不同的操作。

您可能会同意我的观点,但这个概念允许我以更简单的方式演示整个事件循环。这就是为什么许多程序员很容易错误地解释并发编程的异步,就像许多人认为他们正在进行真正的多任务处理一样。

事实是,我们和JS一样,线性地工作,完成一项更大任务的一部分或完成更小的任务并转到另一部分,无限地重复这个过程。因为速度,许多人可能会误认为我们的线性生活方式与多任务。那些更好的多任务者的事件循环运行速度比其他人更快。


异步深入

让我们最后谈谈异步工作流程。您可能知道,AJAX是JS中最广为人知的异步编程采用者。如:

myExampleFunction1(); // 1
ajax("https://example.org"); // 3
myExampleFunction2(); // 2

这个示例AJAX请求可能看起来有点无用,没有任何回调或类似的东西 。

现在,在每一行的旁边,我们有数字,表示给定函数调用完成的顺序。正如您所看到的,即使被调用为第二个,AJAX调用也会成为最后一个调用。为什么?好吧,因为是异步工作。

首先,我们的AJAX函数向服务器发送请求。这就像它的待办事项清单上的一个点。完成后,我们可以进入第三行。因为它是一个同步函数,我们需要在下一个点之前完全完成它。最后,稍后,我们从服务器获得响应,调用我们的回调(如果已定义)并将其推送到我们的事件循环。


回调

回调是一种处理JS中异步工作流的标准方法。回调允许我们定义代码,当给定的异步任务到达定义的点时将执行。让我们回到上一个示例,看看如何将回调应用于该示例。

myExampleFunction1();
ajax("https://example.org", data => {
    console.log(data);
});
myExampleFunction2();

在我们的AJAX函数从服务器获得响应之后,我们的程序将执行提供带有适当数据的回调。这一切似乎都很好,但有一个问题。回调函数有许多问题。其中一个与我们的代码执行的顺序方式不一致。回调会干扰事件循环,因此我们可以确定运行代码的顺序,从而确定结果。由于这种不确定性,又出现了另一个问题。当使用嵌套的异步函数和回调链时,可能会发生这种情况。

ajax("https://example1.org", data1 => {
    console.log(data1);
    ajax("https://example2.org", data2 => {
        console.log(data2);
        ajax("https://example3.org", data2 => {
            console.log(data3);
        });
    });
});

这看起来有点不切实际,但不管你信不信,这种用例和其他嵌套的异步回调都是相当广泛的。现在,我们在这里处理另一个可读性问题。即使使用箭头函数,我们的代码也可能难以阅读,尤其是在添加错误处理和附加逻辑时。


promises

所以,回调并不完美。这就是为什么ECMAScript 6向我们介绍了一种处理异步工作流的革命性方法 - promises。现在,不要将promises视为对每个回调问题执行异步和解决方案的理想方式。虽然promises肯定比回调更好,但它们只是我们已经知道的包装,提供了更好,更好的API。Promise还允许我们创建不会干扰我们自然的顺序控制流的代码。

我认为这对于承诺的优点是足够的,让我们看看这些是什么。来自纯英语的承诺就是 - 承诺。?我们承诺未来的价值。我们来看看API。

const p = new Promise((resolve, reject) => {
    // Call resolve() to resolve and specify value of the promise
    // Call reject() to reject the promise
    resolve(10);
});
p.then(value => {
    // Handle promise being resolve
    console.log(value) // 10
})
p.catch(err => {
    // Handle promise being rejected
})

有了上面的代码和评论,我希望很清楚如何处理承诺。至于我们上面的AJAX函数:

const request = ajax("https://example.org");
request.then(data => {});
request.catch(err => {});

我个人认为promises API非常适合编写和查看,特别是与箭头函数结合使用时。API还为我们提供了Promise.all()Promise.race()


异步未来

我们了解回调和承诺 - 目前主流的JS异步方式。但是,从ES7深处出现了更好的解决方案,称为async / await。使用这两个关键字,您可以轻松编写异步代码,就像使用同步代码一样。我们再次以不同形式的AJAX函数示例:

async function requestData(){
    const data = await ajax("https://example.org");
    return data;
}
const data = requestData();

现在,显然它还没有比承诺更多的语法糖,并且存在一些差异,但基本思想保持不变。Async / await基于所谓的generators。这些是非常高级的主题,在ES6中引入了承诺。它们究竟是什么?好吧,使用最简单的单词,生成器允许我们定义以非线性方式执行的函数,而不是从顶部到底部。更像是以异步方式添加到事件循环中的任务列表。

function *generatorExample {
    console.log("Generator start");
    yield; // Generator pause
    console.log("Generator end");
}
const gen = generatorExample(); // Generator start
gen.next(); // Generator end

因此,正如您所看到的那样,生成器的定义与普通函数一样,前面的符号为*。它们可以与他们的.next()方法一起使用。这些技术在处理自定义和异步迭代器时非常有用,这些迭代器实际上是JavaScript的高级方面,不适用于本文。?


这仅仅是个开始

我希望这篇文章可以帮助您对JS中的异步工作流程有一个基本的了解,提供一个很好的修改或只是娱乐。

译文地址:https://areknawo.com/async-javascript-basics-revise/

©2020 edoou.com   京ICP备16001874号-3