欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

避免地獄async?await的使用及原理解析

 更新時(shí)間:2022年07月04日 16:59:01   作者:智影Yodonicc  
這篇文章主要為大家介紹了避免地獄async?await的使用場(chǎng)景及原理解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

引言

無(wú)論你對(duì)async/await的立場(chǎng)如何,我都想向你說(shuō)明,根據(jù)我的經(jīng)驗(yàn),為什么async/await往往會(huì)使代碼復(fù)雜度更高,而不是更低。

JavaScript中的async/await功能的效用是基于這樣的想法:異步代碼很難,相比之下,同步代碼更容易。這在客觀上是正確的,但在大多數(shù)情況下,我不認(rèn)為async/await真的能解決這個(gè)問(wèn)題。

謊言和async/await

我用來(lái)確定是否要使用某個(gè)模式的指標(biāo)之一是它所帶來(lái)的代碼綜合質(zhì)量。例如,一個(gè)模式可能是干凈的、簡(jiǎn)潔的或廣泛使用的,但如果它導(dǎo)致了容易出錯(cuò)的代碼,它就是一個(gè)我可能會(huì)拒絕的模式。這些模式是雙刃劍,很容易搬起石頭砸自己的腳。

首先,它是建立在一個(gè)謊言之上的。

Async/await讓你的異步代碼看起來(lái)像同步的一樣。

這是它的賣(mài)點(diǎn)。但對(duì)我來(lái)說(shuō),這就是問(wèn)題所在。它從一開(kāi)始就為你的代碼所發(fā)生的事情設(shè)定了錯(cuò)誤的心理模型。同步代碼可能比異步代碼更容易處理,但同步代碼不是異步代碼。它們有非常不同的屬性。

很多時(shí)候這不是問(wèn)題,但當(dāng)它是問(wèn)題時(shí),就很難識(shí)別,因?yàn)閍sync/await正好隱藏了顯示它的線索。以這段代碼為例。

同步代碼

const processData = ({ userData, sessionPrefences }) => {
  save('userData', userData);
  save('session', sessionPrefences);
  return { userData, sessionPrefences }
}

Async/Await

const processData = async ({ userData, sessionPrefences }) => {
  await save('userData', userData);
  await save('session', sessionPrefences);
  return { userData, sessionPrefences }
}

Promise

const processData = ({ userData, sessionPrefences }) =>   save('userData', userData)
  .then(() => save('session', sessionPrefences))
  .then(() => ({ userData, sessionPrefences })

這里有一些性能問(wèn)題。我們已經(jīng)把問(wèn)題縮小到了processData函數(shù)上。在這三種情況中,你對(duì)優(yōu)化途徑的假設(shè)是什么?

我看了第一種情況,發(fā)現(xiàn)我們?cè)趦蓚€(gè)不同的地方保存了兩塊不同的數(shù)據(jù),然后只是返回一個(gè)對(duì)象。唯一可以優(yōu)化的地方是保存函數(shù)。沒(méi)有任何其他選擇。

我看了第二個(gè)例子,也有同樣的想法。唯一可以優(yōu)化的地方是保存函數(shù)。

也許只是因?yàn)槲覍?duì)Promise的太熟悉了,但我看了第三個(gè)例子,我很快看到了一個(gè)機(jī)會(huì)。我看到我們?cè)谶B續(xù)調(diào)用save,盡管其中一個(gè)并不依賴于另一個(gè)。 我們可以將我們的兩個(gè)save調(diào)用并行化。

const processData = ({ userData, sessionPrefences }) => Promise.all([
  save('userData', userData),
  save('session', sessionPrefences)
])
  .then(() => ({ userData, sessionPrefences })

同樣的機(jī)會(huì)也存在于async/await代碼中,只是因?yàn)槲覀兲幱诋惒酱a的思維模式中,所以它被隱藏在明處。在async/await版本中并不是沒(méi)有提示。關(guān)鍵字async和await應(yīng)該給我們同樣的直覺(jué),就像第三個(gè)版本中的then一樣。但我敢打賭,對(duì)許多工程師來(lái)說(shuō),它并沒(méi)有。

為什么沒(méi)有呢?

這是因?yàn)槲覀儽唤虒?dǎo)要以同步的思維方式來(lái)閱讀async/await代碼。在第一個(gè)同步代碼例子中,我們無(wú)法將保存調(diào)用并行化,同樣的邏輯(但現(xiàn)在是不正確的),我們來(lái)到第二個(gè)例子。Async/await將我們的思維置于同步的思維模式中,而這是錯(cuò)誤的思維模式。

此外,如果我們要在async/await的例子中利用并行化的優(yōu)勢(shì),無(wú)論如何我們必須使用promise。

const processData = async ({ userData, sessionPrefences }) => {
  await Promise.all([
    save('userData', userData), 
    save('session',sessionPrefences)
  ])
  return { userData, sessionPrefences }
}

在我看來(lái),如果一個(gè)特定的模式只在一些常見(jiàn)的情況下工作,那么它一定有一些非常大的優(yōu)勢(shì)。如果我不得不在一些非常常見(jiàn)的情況下 "退回"到promise模式,那么我就看不到async/await比promise有什么優(yōu)勢(shì)。對(duì)我來(lái)說(shuō),在多種范式之間切換的認(rèn)知負(fù)擔(dān)并不值得。promise在任何情況下都能完成工作,而且每次都和async/await一樣好,甚至更好。

錯(cuò)誤處理

處理錯(cuò)誤對(duì)于異步代碼來(lái)說(shuō)是至關(guān)重要的。有幾個(gè)關(guān)鍵的地方,我們必須擔(dān)心JavaScript中同步代碼的錯(cuò)誤處理。這主要發(fā)生在我們把一些東西交給本地API,如JSON.parse,或?yàn)g覽器功能,如window.localstorage。

讓我們來(lái)看看我們之前的save函數(shù)的例子,并應(yīng)用一些錯(cuò)誤處理。讓我們假設(shè)在我們的同步例子中,save執(zhí)行了一個(gè)可能會(huì)拋出的操作。這是非常合理的,因?yàn)槿绻4娴絪essionstorage,它可能在序列化或試圖訪問(wèn)sessionstorage的過(guò)程中拋出。為了處理同步代碼中可能出現(xiàn)的錯(cuò)誤,我們通常使用try/catch。

同步代碼

const processData = ({ userData, sessionPrefences }) => {
  try {
    save('userData', userData);
    save('session', sessionPrefences);
    return { userData, sessionPrefences }
  } catch (err) {
    handleErrorSomehow(err)
  }
}

根據(jù)不同的策略,我們可能重新拋出錯(cuò)誤,或者在catch塊中返回一些默認(rèn)值。無(wú)論哪種方式,我們都必須在try塊中封裝任何可能拋出錯(cuò)誤的邏輯。

async/await

由于async/await讓我們 "像看待同步一樣看待async代碼",我們也使用try/catch塊。捕獲塊甚至?xí)⑽覀兊膔eject判定為一個(gè)錯(cuò)誤。

const processData = async ({ userData, sessionPrefences }) => {
  try {
    await save('userData', userData);
    await save('session', sessionPrefences);
    return { userData, sessionPrefences }
  } catch (err) {
    handleErrorSomehow(err)
  }
}

看看這個(gè),async/await實(shí)現(xiàn)了它的承諾。它看起來(lái)與同步版本幾乎完全一樣。

現(xiàn)在,有一些編程流派非常倚重try/catches。我覺(jué)得它們是一種精神上的負(fù)擔(dān)。每當(dāng)有try/catch時(shí),我們現(xiàn)在不僅要擔(dān)心函數(shù)返回什么,還要擔(dān)心它拋出什么。我們不僅有分支邏輯,這增加了復(fù)雜性,而且還必須擔(dān)心同時(shí)處理兩種不同的范式。一個(gè)函數(shù)可以返回一個(gè)值,也可以拋出。因此,每個(gè)函數(shù)都要處理這兩方面的問(wèn)題。這很累人。

try/catch的尷尬

關(guān)于try/catch的最后一點(diǎn)。在JavaScript中,你一般不會(huì)在很多地方看到擁抱try/catch。與其他語(yǔ)言不同的是,在其他語(yǔ)言中,你會(huì)經(jīng)??吹剿热鏙ava。JavaScript中的try塊會(huì)立即將這部分代碼排除在許多引擎優(yōu)化之外,因?yàn)榇a不能再被分解成確定的片段。換句話說(shuō),在JavaScript中,同樣的代碼在被try塊包裹的情況下會(huì)比不被包裹的情況下運(yùn)行得更慢,即使它沒(méi)有拋出的可能性。

Promise

讓我們看看Promise在做什么。

const processData = ({ userData, sessionPrefences }) =>   save('userData', userData)
  .then(() => save('session', sessionPrefences))
  .then(() => ({ userData, sessionPrefences })
  .catch(handleErrorSomehow)

你看到了嗎?

.catch(handleErrorSomehow)

是的。這就是它的全部?jī)?nèi)容。這和其他的方法做的事情完全一樣。我發(fā)現(xiàn)這比try/catch塊更容易閱讀。你覺(jué)得呢?如果同步代碼也這么簡(jiǎn)單就好了......等一下!

const processData = ({ userData, sessionPrefences }) => Promise.resolve(save('userData', userData))
  .then(() => save('session', sessionPrefences))
  .then(() => ({ userData, sessionPrefences })
  .catch(handleErrorSomehow)

好吧,這有缺點(diǎn),但也超級(jí)有趣,你不覺(jué)得嗎?這只是一個(gè)小小的提示,讓你思考如果我們想的話,函數(shù)式風(fēng)格的JavaScript會(huì)是什么樣子。但不管怎樣,接受還是不接受。我的目的是說(shuō)服你使用Promises而不是async/await。而不是承諾Promises全面優(yōu)于async/await。那就太瘋狂了。??

更關(guān)鍵的一點(diǎn)

我想提出的最后一點(diǎn)是。我有時(shí)會(huì)遇到一些論點(diǎn),聲稱async/await可以防止callbacks和promises中可能出現(xiàn)的 "回調(diào)地獄 "現(xiàn)象。

說(shuō)實(shí)話,我第一次聽(tīng)到這種論調(diào)時(shí),我以為這個(gè)人只是混淆了,是想說(shuō) "callbacks"。畢竟,promises設(shè)計(jì)之初的目的之一就是消除 "回調(diào)地獄 "的問(wèn)題,所以我很困惑,人們說(shuō)promises會(huì)導(dǎo)致回調(diào)地獄(我的意思是,它畢竟被稱為回調(diào)(callbacks)地獄,而不是promises地獄)。

但后來(lái)我真的看到了一些promise的代碼,它們看起來(lái)驚人地像回調(diào)地獄。我很困惑,為什么有人會(huì)這樣使用promise。最終,我得出結(jié)論,有些人對(duì)promise的工作原理有一個(gè)非?;镜恼`解。

在我討論這個(gè)問(wèn)題之前,首先讓我承認(rèn),事實(shí)上不可能用async/await創(chuàng)造出金字塔結(jié)構(gòu)的回調(diào)地獄,所以它有這個(gè)優(yōu)勢(shì)。但是我從來(lái)沒(méi)有寫(xiě)過(guò)一個(gè)超過(guò)兩級(jí)的promise流,沒(méi)有必要。當(dāng)然有可能,但從來(lái)沒(méi)有必要。

我發(fā)現(xiàn),每當(dāng)我在promise鏈中看到 "回調(diào)地獄 "時(shí),都是因?yàn)槿藗儧](méi)有意識(shí)到promise的作用就像一個(gè)無(wú)限長(zhǎng)的流程圖。換句話說(shuō),一個(gè)像這樣的流程:

const id = 5
const lotsOAsync = () => fetch('/big-o-list')
  .then((result) => {
    if (result.ok) {
      return result.json().then((list) => {
        const {url: itemURL } = data.items.find((item) => item.id === id)
        return fetch(itemURL).then((result) => {
          if (result.ok) {
            return result.json().then((data) => data.name)
          } else {
            throw new Error(`Couldn't fetch ${itemURL}`)
          }
        })
      })
    } else {
      throw new Error(`Couldn't fetch big-o-list`)
    }
  })

真的應(yīng)該這樣寫(xiě):

const id = 5
const lotsOAsync = () => fetch('/big-o-list')
  .then((result) => result.ok ? result.json() : Promise.reject(`Couldn't fetch big-o-list`))
  .then(({ items }) => items.find((item) => item.id === id))
  .then(({url}) => fetch(url))
  .then((result) => result.ok ? result.json() : Promise.reject(`Couldn't fetch ${result.request.url}`))
  .then((data) => data.name)

如果這有點(diǎn)讓人困惑,讓我給你一個(gè)更簡(jiǎn)單,但更矯情的例子。

回調(diào)地獄 ??

Promise.resolve(
  Promise.resolve(
    Promise.resolve(
      Promise.resolve(
        Promise.resolve(
          Promise.resolve(5)
        )
      )
    )
  )
).then((val) => console.log(val))

promise天堂 ??

Promise.resolve()
  .then(() => Promise.resolve())
  .then(() => Promise.resolve())
  .then(() => Promise.resolve())
  .then(() => Promise.resolve())
  .then(() => Promise.resolve(5))
  .then((val) => console.log(val))

這兩個(gè)例子在創(chuàng)建promise的數(shù)量和順序方面都是一樣的(以及它們實(shí)際上脫離現(xiàn)實(shí),但這是出于學(xué)術(shù)目的,所以我們?cè)试S它)。然而,后一個(gè)比前一個(gè)更有可讀性。

如果你習(xí)慣于寫(xiě)與第一個(gè)例子更像的promise流,讓我給你提供一個(gè)好的小技巧來(lái)擺脫這種習(xí)慣。

每次你想在你的承諾流中寫(xiě)一個(gè)then或catch,首先確保你返回promise,然后轉(zhuǎn)到最外層的promise(如果你一直遵循這個(gè)規(guī)則,那應(yīng)該只有一層)并在那里添加你的then或catch。只要你在返回,你的值就會(huì)冒泡到最外層的promise。這就是你應(yīng)該做的 "then"。

請(qǐng)記住,你不一定要返回一個(gè)Promise來(lái)使用then。一旦你在一個(gè)promise的上下文中,任何返回的值都會(huì)通過(guò)它冒泡。Promise、number、字符串、函數(shù)、對(duì)象,等等。

Promise.resolve(5)
  .then((val) => val + 3)
  .then((num) => String(num))
  .then((str) => `I have ${str} llamas`)
  .then((str) => /ll/.test(str))

這都是完全合法的(盡管有點(diǎn)不合理,但編造的例子是為了說(shuō)清楚??♂)

本文參考Why I avoid async/await,有修改。

總結(jié)

在我看來(lái),promises

  • 提供更好的提示,表明我們處于異步的心理模型中
  • 在簡(jiǎn)單的情況下,對(duì)代碼的表達(dá)至少與async/await一樣干凈。
  • 為包括錯(cuò)誤處理和并行化在內(nèi)的更復(fù)雜的工作流提供了一個(gè)更干凈的選擇。

注:特別感謝技術(shù)指導(dǎo)dazhao(趙達(dá))對(duì)本文的審閱指正。

以上就是避免地獄async await的使用及原理解析的詳細(xì)內(nèi)容,更多關(guān)于避免地獄async await原理的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • JS中this的4種綁定規(guī)則詳解

    JS中this的4種綁定規(guī)則詳解

    如果你學(xué)過(guò)面向?qū)ο缶幊?,那你肯定知道干什么用的,如果你沒(méi)有學(xué)過(guò),那么暫時(shí)可以不用看這篇文章,this既不指向函數(shù)自身,也不指函數(shù)的詞法作用域,具體怎么使用JS中的this,JS中的this綁定規(guī)則又是什么?
    2020-02-02
  • Js原型鏈constructor prototype __proto__屬性實(shí)例詳解

    Js原型鏈constructor prototype __proto__屬性實(shí)例詳解

    這篇文章主要介紹了Js原型鏈constructor prototype __proto__屬性實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-10-10
  • 微信小程序 canvas API詳解及實(shí)例代碼

    微信小程序 canvas API詳解及實(shí)例代碼

    這篇文章主要介紹了微信小程序 canvas API詳解及實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下
    2016-10-10
  • 微信小程序中用WebStorm使用LESS

    微信小程序中用WebStorm使用LESS

    這篇文章主要介紹了微信小程序中用WebStorm使用LESS的相關(guān)資料,需要的朋友可以參考下
    2017-03-03
  • 微信小程序 兩種為對(duì)象屬性賦值的方式詳解

    微信小程序 兩種為對(duì)象屬性賦值的方式詳解

    這篇文章主要介紹了微信小程序 兩種為對(duì)象屬性賦值的方式詳解的相關(guān)資料,需要的朋友可以參考下
    2017-02-02
  • Google 地圖疊加層實(shí)例講解

    Google 地圖疊加層實(shí)例講解

    本文主要介紹Google 地圖疊加層,這里幫大家整理了Google地圖疊加層一些實(shí)用代碼并詳細(xì)介紹了相關(guān)知識(shí),有需要的小伙伴可以參考下
    2016-08-08
  • JavaScript 條件判斷使用技巧詳解

    JavaScript 條件判斷使用技巧詳解

    這篇文章主要為大家介紹了JavaScript 條件判斷使用技巧詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-09-09
  • 最新評(píng)論