代码详解:Async/Await优于基础Promises的7大原因

2022-12-18 0 1,065

概要共4898字,预计今年自学时数10两分钟
代码详解:Async/Await优于基础Promises的7大原因

Async/await已被导入NodeJS 7.6,现阶段能在基本上大部份一流的应用程序上运转。这当然是自2017年年来JavaScript最合适的附带语法结构,没众所周知。

代码详解:Async/Await优于基础Promises的7大原因

Async/Await 101

• Async/await是一类撰写触发器标识符的新方法。从前撰写触发器标识符要用callbacks和promises。

• Async/await 事实上而已一类如前所述promises的果酱语法结构,无法与此基础callbacks或结点callbacks一起采用。

• Async/await和promises那样,都亦然阻塞式的。

• Async/await让触发器标识符极具并行标识符艺术风格,这也是其竞争优势所处。

语法结构

假定表达式getJSON回到两个promise,该promise透过某一JSON第一类展开导出。他们只想初始化它并历史记录该JSON,接着回到“done”。

下列是利用promises继续执行前述关键步骤的形式:

const makeRequest = () => getJSON() .then(data => { console.log(data) return “done” }) makeRequest()

下列则是用async/await的效果:

const makeRequest = async () => { console.log(await getJSON()) return “done” } makeRequest()

两者有一些不同:

1. 表达式前有关键词async。关键词await 只能用于async定义的表达式之内。任一async表达式都能隐式回到promise,其导出值是从该表达式回到的任意值(在此指字符串“done”)。

2. 上一点意味着无法在标识符顶部采用await,因为它不在async定义的表达式范围之中。

// this will not work in top level // await makeRequest() // this will work makeRequest().then((result) => { // do something }) import pandas as pd

3. await getJSON() 意味着 console.log 初始化会等到getJSON() promise展开导出后才输出导出值。

代码详解:Async/Await优于基础Promises的7大原因

为何async/await更优越?

代码详解:Async/Await优于基础Promises的7大原因

1. 简洁干净

看看采用async/await能省去写多少行标识符的麻烦!即使在上面这个人为设计的例子中,也能明显看到省去了相当多行标识符。并且也无需创建两个匿名表达式来处理响应,或为不需要采用的变量提供名称数据,还能避免嵌套标识符。这些小竞争优势快速叠加起来,在下面的标识符示例中变得更加明显。

2. 错误处理

async/ wait能用相同的结构和好用的经典 try/catch处理并行和触发器错误。在下面带有promises的示例中,如果JSON 导出失败,try/catch不会展开处理,因为它要在promise中运转。因此需要在promise中初始化catch并复制错误处理标识符,对生产就绪标识符而言,这将(希望)比console.log更精密。

const makeRequest = () => { try { getJSON() .then(result => { // this parse may fail const data = JSON.parse(result) console.log(data) }) // uncomment this block to handle asynchronous errors // .catch((err) => { // console.log(err) // }) } catch (err) { console.log(err) } }

现在来看看采用async/await的相同标识符。catch块此时就可以处理导出错误。

const makeRequest = async () => { try { // this parse may fail const data = JSON.parse(await getJSON()) console.log(data) } catch (err) { console.log(err) } }

3. 限定条件

const makeRequest = () => { return getJSON() .then(data => { if (data.needsAnotherRequest) { return makeAnotherRequest(data) .then(moreData => { console.log(moreData) return moreData }) } else { console.log(data) return data } }) }

光是看着这些标识符就非常头疼了,很容易就会迷失在各个嵌套(6级)、大括号,以及只用于将最终结果传递到主promise的回到语句中。

但当采用async/ await重写这个示例时,可读性便大大提高。

const makeRequest = async () => { const data = await getJSON() if (data.needsAnotherRequest) { const moreData = await makeAnotherRequest(data); console.log(moreData) return moreData } else { console.log(data) return data } }

4. 中间值

假如初始化promise e1,接着采用它回到的结果初始化promise e2,接着再用这两个promises的结果初始化promise e3。得出的标识符很可能是这样的:

const makeRequest = () => { return promise1() .then(value1 => { // do something return promise2(value1) .then(value2 => { // do something return promise3(value1, value2) }) }) }

如果promise e3不需要值1,就会轻易缩减promise嵌套。如果无法容忍这种情况发生,可以把值1和2都包含在两个promise中,避免更深的嵌套,就像这样:

const makeRequest = () => { return promise1() .then(value1 => { // do something return Promise.all([value1, promise2(value1)]) }) .then(([value1, value2]) => { // do something return promise3(value1, value2) }) }

这种方法为了保证标识符的可读性,牺牲了语义的表达。值1和2除非是为了避免嵌套promise,没理由属于同两个数组。

但采用async/ await,同样的逻辑就会变得异常简单和直观。想想你花了多少时间试图让promises更加简明,而你本可以用这些时间干更多事情!

const makeRequest = async () => { const value1 = await promise1() const value2 = await promise2(value1) return promise3(value1, value2) }

5. 错误堆栈

假定一段标识符在两个链中初始化多个promises,接着在链的某一地方出现了错误。

const makeRequest = () => { return callAPromise() .then(() => callAPromise()) .then(() => callAPromise()) .then(() => callAPromise()) .then(() => callAPromise()) .then(() => { throw new Error(“oops”); }) } makeRequest() .catch(err => { console.log(err); // output // Error: oops at callAPromise.then.then.then.then.then (index.js:8:13) })

从promise链回到的错误堆栈并未显示错误发生的位置。更糟糕的是,它具有误导性。它包含的唯一表达式名是压根没造成错误的callAPromise (但文件和行号仍然有用)。

但async/ await中的错误堆栈能指出包含错误的表达式

const makeRequest = async () => { await callAPromise() await callAPromise() await callAPromise() await callAPromise() await callAPromise() throw new Error(“oops”); } makeRequest() .catch(err => { console.log(err); // output // Error: oops at makeRequest (index.js:7:9) })

如果在本地环境中展开开发并在编辑器中打开文件,这并不是两个很大的加分项。但它在理解来自生产服务器的错误日志方面却非常有用。在这种情况下,知道错误发生在makeRequest中,总好过知道错误来自下一处的下一处的下一处……

6. 调试

使用async/ await的两个极大竞争优势是它更容易调试。调试promises一直是一件痛苦的事情,其原因有二:

(1)无法在回到表达式(没主干)的箭头表达式中设置断点。

代码详解:Async/Await优于基础Promises的7大原因

试试在任意一处设置断点

(2)如果在.then块中设置断点并采用诸如step-over之类的调试快捷键,调试器不会移动到下面的.then块,因为它只能“跨过”并行标识符。

但采用async/ await则无需过多箭头表达式,并且能像正常的并行调用那样直接跨过await初始化。

代码详解:Async/Await优于基础Promises的7大原因

7. 可以await一切

最后,await可用于并行和触发器表达式。例如,await5就相当于Promise.resolve(5)。这个乍一看来似乎没什么用,但在撰写两个库或两个实用表达式时,如果不知道输入是并行还是触发器,这就是两个很大的竞争优势了。

如果希望历史记录在应用程序中继续执行一些API初始化所花费的时间,并为此创建两个泛型表达式,下列是采用promises的效果:

大部份API初始化都会回到promises,但如果采用相同的表达式来历史记录并行表达式所花费的时间,会发生什么?通常情况下会出错,因为并行表达式没回到两个promise。为了避免这种情况,通常会在Promise.resolve()中写入makeRequest()。

但如果采用async/await,就不必担心这些情况,因为await能够安全处理任何值,不管是否有promises。

const recordTime = async (makeRequest) => { const timeStart = Date.now(); await makeRequest(); // works for any sync or async function const timeEnd = Date.now(); console.log(time take:, timeEnd – timeStart); }
代码详解:Async/Await优于基础Promises的7大原因

总结

async/await是过去几年JavaScript上最具革新性的添加特性。它凸显出promises语法结构上的混乱,并提供了简明易懂的替代。

诚然,有人质疑采用async/await会让触发器标识符看起来不那么明显。但每当看见两个初始化或.then块时,就可辨认出触发器标识符。人们也许需要几个星期来适应这种新信号,但C#多年前就具有这个特性了,熟悉的它的人自然明白,这微小、暂时的不便不过是瑕不掩瑜。

代码详解:Async/Await优于基础Promises的7大原因

留言 点赞 关注

他们一起分享AI自学与发展的干货

编译组:杨敏迎、段昌蓉

相关链接:

https://dev.to/gafi/7-reasons-to-always-use-async-await-over-plain-promises-tutorial-4ej9

如需转载,请后台留言,遵守转载规范

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务