5 分钟了解 JavaScript 的 Async + Await
告别 Promise 的初始阶段和回调狂潮!👋🎉
你可能在 JavaScript 中遇到过 Promises(如果你还没有,可以快速查看本指南👍 )。它们允许你钩住异步调用的完成。它们使得链接异步操作甚至将它们组合在一起变得非常简单。但有一个小小的缺点:使用 Promises 时,语法并不总是最优雅的。
引入async + await 🎉
对于TL;DR 阵营的人来说, async
+await
是消耗你的Promise
代码的语法糖 🍭 它们有助于理解代码流程。没有什么新概念,只是Promise
穿着更漂亮的鞋子而已 👟 向下滚动查看gist
⌨️
用代码烤蛋糕🍰
我们要烤蛋糕🍰 真好吃!要烤蛋糕,我们首先需要准备好食材。不好意思,这只是一块普通的海绵蛋糕😅
- 黄油
- 面粉
- 糖
- 鸡蛋🥚
在我们的代码中,获取每种成分都需要异步操作。
例如,方法如下getButter
:
const getButter = () => new Promise((resolve, reject) => {
setTimeout(() => resolve('Butter'), 3000)
})
这些操作将成为方法的一部分getIngredients
。当我们烘焙蛋糕时,我们需要getIngredients
在搅拌之前调用它,等等。
带着承诺
假设我们需要链接每个异步操作。getIngredients
这是一次在超市里挑选一种食材的旅程🛒
大多数情况下,只有当操作相互依赖时,我们才需要将它们链接起来。例如,如果第二个操作需要第一个操作的返回值,依此类推。
在我们的例子中,我们可能一次只能将一件商品添加到购物篮中。这意味着我们需要逐一处理所有配料。请记住,这里的代码是假设的,只是为了展示 Promises 的用法 😉
用 Promise 实现起来会怎么样getIngredients
?我以前肯定见过这种嵌套的 Promise 👀
const getIngredients = () => new Promise((resolve, reject) => {
getButter().then((butter) => {
updateBasket(butter)
getFlour().then((flour) => {
updateBasket(flour)
getSugar().then((sugar) => {
updateBasket(sugar)
getEggs().then((eggs) => {
updateBasket(eggs)
resolve(basket)
})
})
})
})
})
这可以工作,但看起来不太好👎如果使用 Promise 链看起来会更好。
const getIngredients = () => getButter()
.then(updateBasket)
.then(getFlour)
.then(updateBasket)
.then(getSugar)
.then(updateBasket)
.then(getEggs)
.then(updateBasket)
如果我们在网上购物,我们可以使用Promise.all
🤓
const getIngredients = () => Promise.all([
getButter(),
getFlour(),
getSugar(),
getEggs(),
])
这些看起来整洁多了,但我们仍然需要使用回调来获取这些成分。
getIngredients().then(ingredients => doSomethingWithIngredients(ingredients))
使用 async + await 进行整理
让我们来点缀一下语法糖🍭 要使用await
关键字,我们必须先用 关键字将方法声明为异步方法async
。需要注意的是,async
方法总是会返回Promise
。这意味着没有必要返回Promise
🎉
让我们声明getIngredients
为异步
const getIngredients = async () => {}
现在,这些Promise
s 加上语法糖会是什么样子呢?await
关键字允许我们等待 a Promise
,并定义一个变量,该变量的返回值是Promise
。对于这个例子来说,这有点冗长,但让我们把这个语法糖应用到 上getIngredients
。
const getIngredients = async () => {
const butter = await getButter()
const flour = await getFlour()
const sugar = await getSugar()
const eggs = await getEggs()
return [
butter,
flour,
sugar,
eggs,
]
}
代码并没有变小,但更冗长简洁了👍 不再有回调。只有当我们使用 aPromise
时,语法糖才会发挥作用。
const bakeACake = async () => {
const ingredients = await getIngredients()
// do something with the ingredients, no more ".then" 🙌
}
哇!😎 干净到什么程度?
使用async
andawait
使我们的代码更加程序化和全面。它看起来更简洁,功能也完全相同。需要记住的是,我们并没有替换Promise
s,我们仍然在幕后使用它们。现在,我们用一种更简洁的新语法来使用它们。
是的,这也适用Promise.all
。所以,如果我们在线购物,我们的代码会更小。
const getIngredients = async () => {
const ingredients = await Promise.all([
getButter(),
getFlour(),
getSugar(),
getEggs(),
])
return ingredients
}
我们不再需要那个包装函数了!
const getIngredients = async () =>
await Promise.all([getButter(), getFlour(), getSugar(), getEggs()]);
等待非承诺
如果您所指向的值await
不是,该怎么办?在我们的示例中,异步函数在之后Promise
返回。String
setTimeout
const egg = await 🥚
不会有任何错误,该值变为已解决状态Promise
😅
被拒绝了怎么办?
到目前为止,我们已经处理了快乐之路😃但是如果Promise
拒绝的话该怎么办?
比如,如果库存中没有鸡蛋怎么办?我们的异步函数 forgetEggs
将会被拒绝,并可能出现错误。
为了解决这个问题,一个简单的try
/catch
语句就可以了👍
const getIngredients = async () => {
try {
const butter = await 'Butter'
const flour = await getFlour()
const sugar = await getSugar()
const eggs = await getEggs()
return [
butter,
flour,
sugar,
eggs,
]
} catch(e) { return e }
}
我们可以在这个级别或更高级别进行包装,然后调用getIngredients
👍
使用我们的功能并烘焙蛋糕🍰
如果你已经读到这里,我们已经getIngredients
用 new async
+await
关键字创建了 for 函数。剩下的部分看起来会是什么样子呢?
const bakeACake = async () => {
try {
// get the ingredients
const ingredients = await getIngredients()
// mix them together
const cakeMix = await mix(ingredients)
// put in oven on 180C, gas mark 4for 20-25 minutes
const hotCake = await cook(cakeMix)
// allow to stand before serving
const cake = await stand(hotCake)
return cake
} catch (e) { return e }
}
比我们之前用 s 做的要干净得多Promise
🎉
就这样!5分钟用 async + await 烤个蛋糕🍰
如果您已经读到这里,感谢您的阅读😃我整理了一个要点,其中包含一些可能的示例代码,可以在下面看到,以及一些有关async
+的进一步资源await
。
重要的要点⚠️;
async
函数总是返回Promise
await
在大多数情况下将被用来对付一个Promise
或一Promise
组- 使用
try
/catch
语句处理任何潜在错误👍 - 我们还没有谈到这一点,但你可以
await
。await
发出fetch
请求时,你可能会先await
发出请求,然后再发出await
函数json
。
const data = await (await fetch(`${dataUrl}`)).json()
一如既往,如果您有任何问题或建议,请随时留言或发推文给我🐦!记得在社交媒体上关注我😎
const PROB = 0.2 | |
const grabIngredient = ingredient => () => { | |
return new Promise((resolve, reject) => { | |
setTimeout(() => { | |
if (Math.random() > PROB) { | |
resolve(ingredient) | |
} else { | |
reject(`Sorry, we've got no ${ingredient}`) | |
} | |
}, Math.random() * 1000) | |
}) | |
} | |
// boilerplate functions for getting the different ingredients | |
const getButter = grabIngredient('Butter') | |
const getFlour = grabIngredient('Flour') | |
const getSugar = grabIngredient('Sugar') | |
const getEggs = grabIngredient('Eggs') | |
const getIngredientsFromTheSuperMarket = async () => { | |
try { | |
const butter = await getButter() | |
const flour = await getFlour() | |
const sugar = await getSugar() | |
const eggs = await getEggs() | |
return [ | |
butter, | |
flour, | |
sugar, | |
eggs, | |
] | |
} catch(e) { return e } | |
} | |
const getIngredientsOnline = async () => await Promise.all([ | |
getButter(), | |
getFlour(), | |
getSugar(), | |
getEggs(), | |
]) | |
// boilerplate async functions that return strings | |
const mix = async (ingredients) => `Mixing ${ingredients}` | |
const cook = async (cakeMix) => 'Hot Cake' | |
const stand = async (hotCake) => '🍰' | |
const bakeACake = async () => { | |
try { | |
const ingredients = await getIngredientsOnline() | |
const cakeMix = await mix(ingredients) | |
const hotCake = await cook(cakeMix) | |
const cake = await stand(hotCake) | |
console.info('BAKED', cake) | |
return cake | |
} catch (e) { console.info(e) } | |
} | |
bakeACake() |
const PROB = 0.2 | |
const grabIngredient = ingredient => () => { | |
return new Promise((resolve, reject) => { | |
setTimeout(() => { | |
if (Math.random() > PROB) { | |
resolve(ingredient) | |
} else { | |
reject(`Sorry, we've got no ${ingredient}`) | |
} | |
}, Math.random() * 1000) | |
}) | |
} | |
// boilerplate functions for getting the different ingredients | |
const getButter = grabIngredient('Butter') | |
const getFlour = grabIngredient('Flour') | |
const getSugar = grabIngredient('Sugar') | |
const getEggs = grabIngredient('Eggs') | |
const getIngredientsFromTheSuperMarket = async () => { | |
try { | |
const butter = await getButter() | |
const flour = await getFlour() | |
const sugar = await getSugar() | |
const eggs = await getEggs() | |
return [ | |
butter, | |
flour, | |
sugar, | |
eggs, | |
] | |
} catch(e) { return e } | |
} | |
const getIngredientsOnline = async () => await Promise.all([ | |
getButter(), | |
getFlour(), | |
getSugar(), | |
getEggs(), | |
]) | |
// boilerplate async functions that return strings | |
const mix = async (ingredients) => `Mixing ${ingredients}` | |
const cook = async (cakeMix) => 'Hot Cake' | |
const stand = async (hotCake) => '🍰' | |
const bakeACake = async () => { | |
try { | |
const ingredients = await getIngredientsOnline() | |
const cakeMix = await mix(ingredients) | |
const hotCake = await cook(cakeMix) | |
const cake = await stand(hotCake) | |
console.info('BAKED', cake) | |
return cake | |
} catch (e) { console.info(e) } | |
} | |
bakeACake() |
更多资源
await
— MDNasync
函数— MDN- Async + Await 播客— Syntax.fm