async/await:底层原理
内容简介
我对编程语言中的并发策略非常感兴趣,并且由于关于该主题有很多书面研究,所以你可以找到很多策略。
当你查看一些更现代的东西时,你会发现很多关于 相同模式的文献 :async
/ await
。
async
/await
在编程语言中越来越受欢迎,因为它使并发变得非常容易理解和处理。让我们来看看它的工作原理以及它为何如此有用,并使用 JavaScript 来解释这些概念。
我充其量只是个 JavaScript 入门者,但它确实很适合用来解释这些概念。下面的 JS 代码就不用太难看了😅
内容简介
async
/是关于轻松编写并发代码,但更重要的是,它是关于编写易于阅读的await
代码。
三种解决并发问题的方法
这种模式依赖于 Javascript 中称为 Promises 的功能,因此我们将从基础开始构建 JS 中的 Promises,最后将其集成async
到await
Promises 中。
在许多其他语言/框架中,Promise 被称为 Future。有些语言/框架同时使用这两个术语!这可能会让人感到困惑,但概念是一样的。我们将在本文的后面详细介绍。
回调😭
你可能听说过 JavaScript 中的回调函数。如果你还没听说过,它是一种编程模式,允许你安排一些工作,在其他任务完成后再执行。回调函数也是我们这里讨论的基础。
我们在整篇文章中要解决的核心问题是如何在完成一些并发工作之后运行代码。
回调的语法基本上是将一个函数传递给另一个函数:
function doStuff(callback) {
// do something
// now it's done, call the callback
callback(someStuff)
}
doStuff(function(result) {
// when doStuff is done doing its thing, it'll pass its result
// to this function.
//
// we don't know when that'll be, just that this function will run.
//
// That means that the rest of our ENTIRE PROGRAM needs to go in here
// (most of the time)
//
// Barf, amirite?
console.log("done with doStuff");
});
// Wait, though... if you put something here ... it'll run right away. It won't wait for doStuff to finish
代码中最后那句注释最让人困惑。实际上,大多数应用并不想继续执行,而是想等待。回调函数让这一点难以实现,令人困惑,而且写起来读起来也很费劲😞。
承诺🙌
我会看看你的回调函数,然后给你加个分Promise
!其实不然,Promise 只是装扮好的回调函数,让事情更容易处理。但你仍然需要将函数传递给函数,这比实际要难一些。
function returnAPromiseYall() {
// do some stuff!
return somePromise;
}
// let's call it and get our promise
let myProm = returnAPromiseYall();
// now we have to do some stuff after the promise is ready
myProm.then(function(result) {
// the result is the variable in the promise that we're waiting for,
// just like in callback world
return anotherPromise;
}).then(function(newResult) {
// We can chain these "then" calls together to build a pipeline of
// code. So it's a little easier to read, but still.
// Passing functions to functions and remembering to write your code inside
// these "then" calls is sorta tiring
doMoreStuff(newResult);
});
我们取得了一些小胜利:
- 不再有令人畏惧的嵌套回调
- 这个
then
函数意味着一个代码流水线。从语法和概念上来说,这更容易处理
但我们仍然面临一些棘手的问题:
- 你必须记住把程序的其余部分放入
then
- 你仍然在将函数传递给函数。读写起来仍然很累。
异步/等待🥇
好了,各位,我们到了!Promise
d 之地 🎉🥳🍤。我们可以摆脱函数与函数之间的传递then
,以及忘记将程序的其余部分放入then
.
全部采用此🔥模式。查看:
async function doStuff() {
// just like the last two examples, return a promise
return myPromise;
}
// now, behold! we can call it with await
let theResult = await doStuff();
// IN A WORLD, WHERE THERE ARE NO PROMISES ...
// ONLY GUARANTEES
//
// In other words, the value is ready right here!
console.log(`the result is ready: ${theResult}`);
得益于这个await
关键字,我们可以从上到下阅读代码。这些代码会在底层被翻译成其他形式,具体是什么取决于语言。在 JS 领域,它基本上Promise
大部分时间都是 s 。然而,对于我们程序员来说,结果总是一样的:
- 程序员可以像我们习惯的那样,从上到下读写代码
- 无需将函数传递给函数,
})
这意味着更少的语法忘记写 - 关键字
await
可以是一个指示doStuff
执行某些“昂贵”操作的指标(例如调用 REST API)
那么async
关键词呢?
async
在包括 JS 在内的许多语言中,如果函数内部使用了某个函数,则必须对其进行标记await
。这样做有其特定于语言的原因,但以下是一些你应该注意的事项:
- 告诉调用者其中有
Promise
s 或s 正在发生await
- 告诉运行时(或其他语言中的编译器)在后台发挥其魔力以“使其工作”™
🏁
就是这样。我省略了很多实现细节,但务必记住,这种模式的存在更多是出于人为原因,而非技术原因。
你可以用回调函数完成所有这些操作,但几乎所有情况下,async
/await
都能让你的生活更轻松。尽情享受吧!👋