国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

async語(yǔ)法升級(jí)踩坑小記

VioletJack / 2666人閱讀

摘要:普通的回調(diào)函數(shù)調(diào)用執(zhí)行后續(xù)邏輯使用了以后的復(fù)雜邏輯獲取到正確的結(jié)果輸出兩個(gè)文件拼接后的內(nèi)容雖說(shuō)解決了的問(wèn)題,不會(huì)出現(xiàn)一個(gè)函數(shù)前邊有二三十個(gè)空格的縮進(jìn)。所以直接使用關(guān)鍵字替換原有的普通回調(diào)函數(shù)即可。

從今年過(guò)完年回來(lái),三月份開始,就一直在做重構(gòu)相關(guān)的事情。  
就在今天剛剛上線了最新一次的重構(gòu)代碼,希望高峰期安好,接近半年的Node.js代碼重構(gòu)。
包含從callback+async.waterfallgenerator+co,統(tǒng)統(tǒng)升級(jí)為了async,還順帶推動(dòng)了TypeScript在我司的使用。
這些日子也踩了不少坑,也總結(jié)了一些小小的優(yōu)化方案,進(jìn)行精簡(jiǎn)后將一些比較關(guān)鍵的點(diǎn),拿出來(lái)分享給大家,希望有同樣在做重構(gòu)的小伙伴們可以繞過(guò)這些。
為什么要升級(jí)

首先還是要談?wù)劯拇a的理由,畢竟重構(gòu)肯定是要有合理的理由的。
如果單純想看升級(jí)相關(guān)事項(xiàng)可以直接選擇跳過(guò)這部分。

Callback

從最原始的開始說(shuō)起,期間確實(shí)遇到了幾個(gè)年代久遠(yuǎn)的項(xiàng)目,Node 0.x,使用的普通callback,也有一些會(huì)應(yīng)用上async.waterfall這樣在當(dāng)年看起來(lái)很優(yōu)秀的工具。

// 普通的回調(diào)函數(shù)調(diào)用
var fs = require("fs")

fs.readFile("test1.txt", function (err, data1) {
  if (err) return console.error(err)


  fs.readFile("test2.txt", function (err, data2) {
    if (err) return console.error(err)

    // 執(zhí)行后續(xù)邏輯
    console.log(data1.toString() + data2.toString())
    // ...
  })
})

// 使用了async以后的復(fù)雜邏輯
var async = require("fs")

async.waterfall([
  function (callback) {
    fs.readFile("test1.txt", function (err, data) {
      if (err) callback(err)

      callback(null, data.toString())
    })
  },
  function (result, callback) {
    fs.readFile("test2.txt", function (err, data) {
      if (err) callback(err)

      callback(null, result + data.toString())
    })
  }
], function (err, result) {
  if (err) return console.error(err)

  // 獲取到正確的結(jié)果
  console.log(result) // 輸出兩個(gè)文件拼接后的內(nèi)容
})

雖說(shuō)async.waterfall解決了callback hell的問(wèn)題,不會(huì)出現(xiàn)一個(gè)函數(shù)前邊有二三十個(gè)空格的縮進(jìn)。
但是這樣的流程控制在某些情況下會(huì)讓代碼變得很詭異,例如我很難在某個(gè)函數(shù)中選擇下一個(gè)應(yīng)該執(zhí)行的函數(shù),而是只能按照順序執(zhí)行,如果想要進(jìn)行跳過(guò),可能就要在中途的函數(shù)中進(jìn)行額外處理:

async.waterfall([
  function (callback) {
    if (XXX) {
      callback(null, null, null, true)
    } else {
      callback(null, data1, data2)
    }
  },
  function (data1, data2, isPass, callback) {
    if (isPass) {
      callback(null, null, null, isPass)
    } else {
      callback(null, data1 + data2)
    }
  }
])

所以很可能你的代碼會(huì)變成這樣,里邊存在大量的不可讀的函數(shù)調(diào)用,那滿屏充斥的null占位符。

所以callback這種形式的,一定要進(jìn)行修改, __這屬于難以維護(hù)的代碼__。

Generator

實(shí)際上generator是依托于co以及類似的工具來(lái)實(shí)現(xiàn)的將其轉(zhuǎn)換為Promise,從編輯器中看,這樣的代碼可讀性已經(jīng)沒(méi)有什么問(wèn)題了,但是問(wèn)題在于他始終是需要額外引入co來(lái)幫忙實(shí)現(xiàn)的,generator本身并不具備幫你執(zhí)行異步代碼的功能。
不要再說(shuō)什么async/await是generator的語(yǔ)法糖了

因?yàn)槲宜?b>Node版本已經(jīng)統(tǒng)一升級(jí)到了8.11.x,所以async/await語(yǔ)法已經(jīng)可用。
這就像如果document.querySelectorAll、fetch已經(jīng)可以滿足需求了,為什么還要引入jQuery呢。

所以,將generator函數(shù)改造為async/await函數(shù)也是勢(shì)在必行。

期間遇到的坑

callback的升級(jí)為async/await其實(shí)并沒(méi)有什么坑,反倒是在generator + co 那里遇到了一些問(wèn)題:

數(shù)組執(zhí)行的問(wèn)題

co的代碼中,大家應(yīng)該都見到過(guò)這樣的:

const results = yield list.map(function * (item) {
  return yield getData(item)
})

在循環(huán)中發(fā)起一些異步請(qǐng)求,有些人會(huì)告訴你,從yield改為async/await僅僅替換關(guān)鍵字就好了。

那么恭喜你得到的results實(shí)際上是一個(gè)由Promise實(shí)例組成的數(shù)組。

const results = await list.map(async item => {
  return await getData(item)
})

console.log(results) // [Promise, Promise, Promise, ...]

因?yàn)?b>async并不會(huì)判斷你后邊的是不是一個(gè)數(shù)組(這個(gè)是在co中有額外的處理)而僅僅檢查表達(dá)式是否為一個(gè)Promise實(shí)例。
所以正確的做法是,添加一層Promise.all,或者說(shuō)等新的語(yǔ)法await*,Node.js 10.x貌似還不支持。。

// 關(guān)于這段代碼的優(yōu)化方案在下邊的建議中有提到
const results = await Promise.all(list.map(async item => {
  return await getData(item)
}))

console.log(results) // [1, 2, 3, ...]
await / yield 執(zhí)行順序的差異

這個(gè)一般來(lái)說(shuō)遇到的概率不大,但是如果真的遇到了而栽了進(jìn)去就欲哭無(wú)淚了。

首先這樣的代碼在執(zhí)行上是沒(méi)有什么區(qū)別的:

yield 123 // 123

await 123 // 123

這樣的代碼也是沒(méi)有什么區(qū)別的:

yield Promise.resolve(123) // 123

await Promise.resolve(123) // 123

但是這樣的代碼,問(wèn)題就來(lái)了:

yield true ? Promise.resolve(123) : Promise.resolve(233) // 123

await true ? Promise.resolve(123) : Promise.resolve(233) // Promise<123>

從字面上我們其實(shí)是想要得到yield那樣的效果,結(jié)果卻得到了一個(gè)Promise實(shí)例。
這個(gè)是因?yàn)?b>yield、await兩個(gè)關(guān)鍵字執(zhí)行順序不同所導(dǎo)致的。

在MDN的文檔中可以找到對(duì)應(yīng)的說(shuō)明:MDN | Operator precedence

可以看到yield的權(quán)重非常低,僅高于return,所以從字面上看,這個(gè)執(zhí)行的結(jié)果很符合我們想要的。
await關(guān)鍵字的權(quán)重要高很多,甚至高于最普通的四則運(yùn)算,所以必然也是高于三元運(yùn)算符的。

也就是說(shuō)await版本的實(shí)際執(zhí)行是這樣子的:

(await true) ? Promise.resolve(123) : Promise.resolve(233) // Promise<123>

那么我們想要獲取預(yù)期的結(jié)果,就需要添加()來(lái)告知解釋器我們想要的執(zhí)行順序了:

await (true ? Promise.resolve(123) : Promise.resolve(233)) // 123
一定不要漏寫 await 關(guān)鍵字

這個(gè)其實(shí)算不上升級(jí)時(shí)的坑,在使用co時(shí)也會(huì)遇到,但是這是一個(gè)很嚴(yán)重,而且很容易出現(xiàn)的問(wèn)題。

如果有一個(gè)異步的操作用來(lái)返回一個(gè)布爾值,告訴我們他是否為管理員,我們可能會(huì)寫這樣的代碼:

async function isAdmin (id) {
  if (id === 123) return true

  return false
}

if (await isAdmin(1)) {
  // 管理員的操作
} else {
  // 普通用戶的操作
}

因?yàn)檫@種寫法接近同步代碼,所以遺漏關(guān)鍵字是很有可能出現(xiàn)的:

if (isAdmin(1)) {
  // 管理員的操作
} else {
  // 普通用戶的操作
}

因?yàn)?b>async函數(shù)的調(diào)用會(huì)返回一個(gè)Promise實(shí)例,得益于我強(qiáng)大的弱類型腳本語(yǔ)言,Promise實(shí)例是一個(gè)Object,那么就不為空,也就是說(shuō)會(huì)轉(zhuǎn)換為true,那么所有調(diào)用的情況都會(huì)進(jìn)入if塊。

那么解決這樣的問(wèn)題,有一個(gè)比較穩(wěn)妥的方式,強(qiáng)制判斷類型,而不是簡(jiǎn)單的使用if else,使用類似(a === 1)(a === true)這樣的操作。_eslint、ts 之類的都很難解決這個(gè)問(wèn)題_

一些建議 何時(shí)應(yīng)該用 async ,何時(shí)應(yīng)該直接用 Promise

首先,async函數(shù)的執(zhí)行返回值就是一個(gè)Promise,所以可以簡(jiǎn)單地理解為async是一個(gè)基于Promise的包裝:

function fetchData () {
  return Promise().resolve(123)
}

// ==>

async function fetchData () {
  return 123
}

所以可以認(rèn)為說(shuō)await后邊是一個(gè)Promise的實(shí)例。
而針對(duì)一些非Promise實(shí)例則沒(méi)有什么影響,直接返回?cái)?shù)據(jù)。

在針對(duì)一些老舊的callback函數(shù),當(dāng)前版本的Node已經(jīng)提供了官方的轉(zhuǎn)換工具util.promisify,用來(lái)將符合Error-first callback規(guī)則的異步操作轉(zhuǎn)換為Promise實(shí)例:

而一些沒(méi)有遵守這樣規(guī)則的,或者我們要自定義一些行為的,那么我們會(huì)嘗試手動(dòng)實(shí)現(xiàn)這樣的封裝。
在這種情況下一般會(huì)采用直接使用Promise,因?yàn)檫@樣我們可以很方便的控制何時(shí)應(yīng)該reject,何時(shí)應(yīng)該resolve

但是如果遇到了在回調(diào)執(zhí)行的過(guò)程中需要發(fā)起其他異步請(qǐng)求,難道就因?yàn)檫@個(gè)Promise導(dǎo)致我們?cè)趦?nèi)部也要使用.then來(lái)處理么?

function getList () {
  return new Promise((resolve, reject) => {
    oldMethod((err, data) => {
      fetch(data.url).then(res => res.json()).then(data => {
        resolve(data)
      })
    })
  })
}

await getList()

但上邊的代碼也太丑了,所以關(guān)于上述問(wèn)題,肯定是有更清晰的寫法的,不要限制自己的思維。
__async也是一個(gè)普通函數(shù)__,完全可以放在任何函數(shù)執(zhí)行的地方。

所以關(guān)于上述的邏輯可以進(jìn)行這樣的修改:

function getList () {
  return new Promise((resolve, reject) => {
    oldMethod(async (err, data) => {
      const res = await fetch(data.url)
      const data = await res.json()

      resolve(data)
    })
  })
}

await getList()

這完全是一個(gè)可行的方案,對(duì)于oldMethod來(lái)說(shuō),我按照約定調(diào)用了傳入的回調(diào)函數(shù),而對(duì)于async匿名函數(shù)來(lái)說(shuō),也正確的執(zhí)行了自己的邏輯,并在其內(nèi)部觸發(fā)了外層的resolve,實(shí)現(xiàn)了完整的流程。

代碼變得清晰很多,邏輯沒(méi)有任何修改。

合理的減少 await 關(guān)鍵字

await只能在async函數(shù)中使用,await后邊可以跟一個(gè)Promise實(shí)例,這個(gè)是大家都知道的。
但是同樣的,有些await其實(shí)并沒(méi)有存在的必要。

首先有一個(gè)我面試時(shí)候經(jīng)常會(huì)問(wèn)的題目:

Promise.resolve(Promise.resolve(123)).then(console.log) // ?

最終輸出的結(jié)果是什么。

這就要說(shuō)到resolve的執(zhí)行方式了,如果傳入的是一個(gè)Promise實(shí)例,亦或者是一個(gè)thenable對(duì)象(_簡(jiǎn)單的理解為支持.then((resolve, reject) => {})調(diào)用的對(duì)象_),那么resolve實(shí)際返回的結(jié)果是內(nèi)部執(zhí)行的結(jié)果。
也就是說(shuō)上述示例代碼直接輸出123,哪怕再多嵌套幾層都是一樣的結(jié)果。

通過(guò)上邊所說(shuō)的,不知大家是否理解了 合理的減少 await 關(guān)鍵字 這句話的意思。

結(jié)合著前邊提到的在async函數(shù)中返回?cái)?shù)據(jù)是一個(gè)類似Promise.resolve/Promise.reject的過(guò)程。
await就是類似監(jiān)聽then的動(dòng)作。

所以像類似這樣的代碼完全可以避免:

const imgList = []

async function getImage (url) {
  const res = await fetch(url)

  return await res.blob()
}

await Promise.all(imgList.map(async url => await getImage(url)))

// ==>

async function getImage (url) {
  const res = fetch(url)

  return res.blob()
}

await Promise.all(imgList.map(url => getImage(url)))

上下兩種方案效果完全相同。

Express 與 koa 的升級(jí)

首先,Express是通過(guò)調(diào)用response.send來(lái)完成請(qǐng)求返回?cái)?shù)據(jù)的。
所以直接使用async關(guān)鍵字替換原有的普通回調(diào)函數(shù)即可。

Koa也并不是說(shuō)你必須要升級(jí)到2.x才能夠使用async函數(shù)。
Koa1.x中推薦的是generator函數(shù),也就意味著其內(nèi)部是調(diào)用了co來(lái)幫忙做轉(zhuǎn)換的。
而看過(guò)co源碼的小伙伴一定知道,里邊同時(shí)存在對(duì)于Promise的處理。
也就是說(shuō)傳入一個(gè)async函數(shù)完全是沒(méi)有問(wèn)題的。

但是1.x的請(qǐng)求上下文使用的是this,而2.x則是使用的第一個(gè)參數(shù)context
所以在升級(jí)中這里可能是唯一需要注意的地方,__在1.x不要使用箭頭函數(shù)來(lái)注冊(cè)中間件__。

// express
express.get("/", async (req, res) => {
  res.send({
    code: 200
  })
})

// koa1.x
router.get("/", async function (next) {
  this.body = {
    code: 200
  }
})

// koa2.x
router.get("/", async (ctx, next) => {
  ctx.body = {
    code: 200
  }
})
小結(jié)

重構(gòu)項(xiàng)目是一件很有意思的事兒,但是對(duì)于一些注釋文檔都很缺失的項(xiàng)目來(lái)說(shuō),重構(gòu)則是一件痛苦的事情,因?yàn)槟阈枰獜拇a中獲取邏輯,而作為動(dòng)態(tài)腳本語(yǔ)言的JavaScript,其在大型項(xiàng)目中的可維護(hù)性并不是很高。
所以如果條件允許,還是建議選擇TypeScript之類的工具來(lái)幫助更好的進(jìn)行開發(fā)。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/98033.html

相關(guān)文章

  • HTTP訪問(wèn)控制(CORS)踩坑小記

    摘要:前幾天在幫后端排查一個(gè)的問(wèn)題的時(shí)候發(fā)現(xiàn)的一些小坑特此記錄的本質(zhì)是出于安全原因,瀏覽器限制從腳本內(nèi)發(fā)起的跨源請(qǐng)求。排查發(fā)現(xiàn)訪問(wèn)失敗的都是需要用戶的登錄態(tài)的。 前幾天在幫后端排查一個(gè)cors的問(wèn)題的時(shí)候發(fā)現(xiàn)的一些小坑特此記錄 ** cors的本質(zhì)是出于安全原因,瀏覽器限制從腳本內(nèi)發(fā)起的跨源HTTP請(qǐng)求。 例如,XMLHttpRequest和FetchAPI遵循同源策略。 這意味著使用這些A...

    xiongzenghui 評(píng)論0 收藏0
  • HTTP訪問(wèn)控制(CORS)踩坑小記

    摘要:前幾天在幫后端排查一個(gè)的問(wèn)題的時(shí)候發(fā)現(xiàn)的一些小坑特此記錄的本質(zhì)是出于安全原因,瀏覽器限制從腳本內(nèi)發(fā)起的跨源請(qǐng)求。排查發(fā)現(xiàn)訪問(wèn)失敗的都是需要用戶的登錄態(tài)的。 前幾天在幫后端排查一個(gè)cors的問(wèn)題的時(shí)候發(fā)現(xiàn)的一些小坑特此記錄 ** cors的本質(zhì)是出于安全原因,瀏覽器限制從腳本內(nèi)發(fā)起的跨源HTTP請(qǐng)求。 例如,XMLHttpRequest和FetchAPI遵循同源策略。 這意味著使用這些A...

    wujl596 評(píng)論0 收藏0
  • FEDay 參會(huì)小記

    摘要:介紹微信風(fēng)格的,與客戶端體驗(yàn)一致,這個(gè)自己去微信上看吧,略。微信調(diào)試一件套,網(wǎng)頁(yè)授權(quán)模擬集成代理遠(yuǎn)程調(diào)試。這些在微信開發(fā)者中心有介紹,略。年微信開發(fā)經(jīng)驗(yàn)的人,終于又成為了零年開發(fā)經(jīng)驗(yàn)的人,重新走上了踩坑之路。 showImg(https://segmentfault.com/img/bVtEd1);活動(dòng)地址:http://fequan.com/2016/ 注意:英文不好,小記也帶有自己...

    xcc3641 評(píng)論0 收藏0
  • react-router 升級(jí)小記

    摘要:前言最近將公司項(xiàng)目的從版本升到了版本,跟完全不兼容,是一次徹底的重寫。升級(jí)過(guò)程中踩了不少的坑,也有一些值得分享的點(diǎn)。沒(méi)有就會(huì)匹配所有路由最后不得不說(shuō)升級(jí)很困難,坑也很多。 前言 最近將公司項(xiàng)目的 react-router 從 v3 版本升到了 v4 版本,react-router v4 跟 v3 完全不兼容,是一次徹底的重寫。這也給升級(jí)造成了極大的困難,與其說(shuō)升級(jí)不如說(shuō)是對(duì) route...

    isLishude 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<