lodash已死?Radash庫方法介紹及原始碼解析 —— 非同步方法篇

雾散声声慢發表於2024-05-14

寫在前面

tips:點贊 + 收藏 = 學會!

  • 我們前面已經介紹了 radash 的相關資訊和所有 Array 相關方法,詳情可前往主頁檢視。
  • 本篇我們繼續介紹radash中非同步相關的方法。
  • 所有方法分享完畢後,後續作者也會整理出 Radash 庫所有方法的使用目錄,包括文章說明腦圖說明
    • 因為方法較多,後續將專門釋出一篇總結文件,方便大家查閱使用。
    • 所有方法的思維導圖說明後續也會上傳至 github 和 gitee,有需要的可以訪問下載。

all:同時執行多個非同步操作

  1. 使用說明
    1. 功能描述: 類似於 Promise.allPromise.allSettled,等待一個由多個 Promise 組成的物件或陣列中的所有 Promise 都完成(或者其中一個失敗)。執行的所有錯誤和丟擲的錯誤都會收集在 AggregateError 中。
    2. 引數:promise物件/promise陣列
    3. 返回值:所有 promise 執行後的結果陣列或物件
  2. 使用程式碼示例
    import { all } from 'radash'
    
    // 傳入promise陣列
    const [user] = await all([
      api.users.create(...),
      s3.buckets.create(...),
      slack.customerSuccessChannel.sendMessage(...)
    ])
    
    // 傳入物件
    const { user } = await all({
      user: api.users.create(...),
      bucket: s3.buckets.create(...),
      message: slack.customerSuccessChannel.sendMessage(...)
    })
    
  3. 原始碼解析
    // 定義一個泛型非同步函式 `all`。
    export async function all<
      // 泛型約束 `T` 可以是一個 `Promise` 陣列或一個 `Promise` 物件。
      T extends Record<string, Promise<any>> | Promise<any>[]
    >(promises: T) {
      // 根據 `promises` 是陣列還是物件,將其轉換成一個統一格式的陣列 `entries`。
      const entries = isArray(promises)
        ? promises.map(p => [null, p] as [null, Promise<any>])
        : Object.entries(promises)
    
      // 使用 `Promise.all` 等待所有 `Promise` 完成,並處理每個 `Promise` 的結果和異常。
      const results = await Promise.all(
        entries.map(([key, value]) =>
          value
            .then(result => ({ result, exc: null, key })) // 如果成功,記錄結果。
            .catch(exc => ({ result: null, exc, key })) // 如果失敗,記錄異常。
        )
      )
    
      // 篩選出所有出現異常的結果。
      const exceptions = results.filter(r => r.exc)
      // 如果有異常,丟擲一個 `AggregateError`,包含所有異常。
      if (exceptions.length > 0) {
        throw new AggregateError(exceptions.map(e => e.exc))
      }
    
      // 如果輸入的 `promises` 是陣列,返回一個包含所有結果的陣列。
      if (isArray(promises)) {
        return results.map(r => r.result) as T extends Promise<any>[]
          ? PromiseValues<T>
          : unknown
      }
    
      // 如果輸入的 `promises` 是物件,將結果組合成一個新物件並返回。
      return results.reduce(
        (acc, item) => ({
          ...acc,
          [item.key!]: item.result // 使用斷言 `item.key!`,因為我們知道 `key` 不會是 `null`。
        }),
        {} as { [K in keyof T]: Awaited<T[K]> } // 返回型別是一個物件,其鍵型別為 `T` 的鍵,值型別為 `T` 中 `Promise` 解析後的型別。
      )
    }
    
    • 方法流程說明:
      1. 將輸入的 promises 轉換為一個統一格式的 entries 陣列,無論它是一個 Promise 陣列還是一個 Promise 物件。
      2. 對於每個 entry,建立一個新的 Promise 來處理成功和失敗的情況,並使用 Promise.all 等待所有這些新 Promise 完成。
      3. 如果所有 Promise 都成功解析,根據 promises 是陣列還是物件,返回一個包含所有結果的陣列或物件。
      4. 如果有一個或多個 Promise 失敗,則丟擲一個 AggregateError,其中包含所有失敗的 Promise 的異常。

defer:在非同步流程中新增清理或錯誤處理邏輯

  1. 使用說明
    1. 功能描述:用來執行一個非同步函式,同時提供註冊回撥的機制,在非同步函式執行完成後執行特定回撥操作。
    2. 引數:非同步函式。
    3. 返回值:非同步函式成功執行時,返回其響應結果,否則重新丟擲錯誤。
  2. 使用程式碼示例
    import { defer } from 'radash'
    
    await defer(async (cleanup) => {
      const buildDir = await createBuildDir()
    
      cleanup(() => fs.unlink(buildDir))
    
      await build()
    })
    
    await defer(async (register) => {
      const org = await api.org.create()
      register(async () => api.org.delete(org.id), { rethrow: true })
    
      const user = await api.user.create()
      register(async () => api.users.delete(user.id), { rethrow: true })
    
      await executeTest(org, user)
    })
    
  3. 原始碼解析
    // 定義一個非同步泛型函式 `defer`。
    export const defer = async <TResponse>(
      // `func` 是一個接受註冊函式 `register` 的非同步函式。
      func: (
        register: (
          // `register` 允許 `func` 註冊一個回撥函式 `fn`,該函式在 `func` 執行完成後呼叫。
          // 可以透過 `options` 指定是否在回撥函式中重新丟擲錯誤。
          fn: (error?: any) => any,
          options?: { rethrow?: boolean }
        ) => void
      ) => Promise<TResponse>
    ): Promise<TResponse> => {
      // 初始化一個用於存放回撥函式及其選項的陣列 `callbacks`。
      const callbacks: {
        fn: (error?: any) => any
        rethrow: boolean
      }[] = []
    
      // 實現註冊函式 `register`,它將回撥函式及其選項新增到 `callbacks` 陣列。
      const register = (
        fn: (error?: any) => any,
        options?: { rethrow?: boolean }
      ) =>
        callbacks.push({
          fn,
          rethrow: options?.rethrow ?? false
        })
    
      // 呼叫 `tryit` 函式執行 `func`,並傳入 `register`。
      // `tryit` 函式不在提供的程式碼片段中,但我們可以假設它是一個錯誤處理函式,返回一個包含錯誤和響應的元組。
      const [err, response] = await tryit(func)(register)
    
      // 遍歷 `callbacks` 陣列,依次執行每個回撥函式。
      for (const { fn, rethrow } of callbacks) {
        // 使用 `tryit` 函式呼叫回撥,以捕獲並處理任何丟擲的錯誤。
        const [rethrown] = await tryit(fn)(err)
        // 如果回撥函式中有錯誤被重新丟擲,並且 `rethrow` 選項為 `true`,則重新丟擲該錯誤。
        if (rethrown && rethrow) throw rethrown
      }
    
      // 如果 `func` 執行時有錯誤產生,重新丟擲該錯誤。
      if (err) throw err
      // 如果 `func` 執行成功,返回響應結果。
      return response
    }
    
    • 方法流程說明:
      1. 定義一個 callbacks 陣列來儲存註冊的回撥函式及其選項。
      2. 實現 register 函式,該函式允許 func 註冊回撥函式和選項。
      3. 呼叫外部提供的(但在程式碼片段中未定義)tryit 函式執行 func,並傳遞 register 函式給 func
      4. 等待 func 完成執行,獲取可能的錯誤 err 和響應 response
      5. 依次執行 callbacks 陣列中的回撥函式,處理可能的錯誤。
      6. 如果任何一個回撥函式中出現需要重新丟擲的錯誤,並且其 rethrow 選項為 true,則重新丟擲該錯誤。
      7. 如果 func 執行時產生了錯誤,重新丟擲該錯誤。
      8. 如果 func 成功執行,返回其響應結果。

guard:執行一個函式,並提供錯誤處理的能力

  1. 使用說明
    1. 功能描述:guard 函式可以用來為函式呼叫提供額外的錯誤處理邏輯,特別是當你希望根據錯誤型別選擇性地處理錯誤時。
    2. 引數:目標函式、指定錯誤物件得函式(可選)。
    3. 返回值:丟擲原始或返回undefined。
  2. 使用程式碼示例
    import { guard } from 'radash'
    
    const users = (await guard(fetchUsers)) ?? []
    
    const isInvalidUserError = (err: any) => err.code === 'INVALID_ID'
    const user = (await guard(fetchUser, isInvalidUserError)) ?? DEFAULT_USER
    
  3. 原始碼解析
    // 定義一個泛型函式 `guard`。
    export const guard = <TFunction extends () => any>(
      // 引數 `func` 是一個無引數的函式,它可能返回任何型別的值,包括 `Promise`。
      func: TFunction,
      // 可選引數 `shouldGuard` 是一個函式,它接受一個錯誤物件 `err`,
      // 並返回一個布林值,指示是否應該 "guard" 這個錯誤。
      shouldGuard?: (err: any) => boolean
    // 函式的返回型別依賴於 `func` 的返回型別。如果 `func` 返回一個 `Promise`,
    // 則 `guard` 返回一個 `Promise`,該 `Promise` 解析為 `func` 的返回值或 `undefined`。
    // 如果 `func` 不返回 `Promise`,則 `guard` 返回 `func` 的返回值或 `undefined`。
    ): ReturnType<TFunction> extends Promise<any>
      ? Promise<Awaited<ReturnType<TFunction>> | undefined>
      : ReturnType<TFunction> | undefined => {
      // 定義一個內部函式 `_guard`,它接受一個錯誤物件 `err`。
      const _guard = (err: any) => {
        // 如果提供了 `shouldGuard` 函式並且該函式返回 `false`,
        // 表示不應該 "guard" 這個錯誤,則重新丟擲該錯誤。
        if (shouldGuard && !shouldGuard(err)) throw err
        // 否則,返回 `undefined`。
        return undefined as any
      }
    
      // 定義一個型別守衛函式 `isPromise`,它檢查一個值是否為 `Promise`。
      const isPromise = (result: any): result is Promise<any> =>
        result instanceof Promise
    
      try {
        // 嘗試執行 `func` 並獲取結果。
        const result = func()
        // 如果 `result` 是一個 `Promise`,使用 `catch` 方法應用 `_guard` 函式。
        // 否則,直接返回 `result`。
        return isPromise(result) ? result.catch(_guard) : result
      } catch (err) {
        // 如果在執行 `func` 時丟擲錯誤,使用 `_guard` 函式處理該錯誤。
        return _guard(err)
      }
    }
    
    • 方法流程說明:
      1. 嘗試執行 func 函式並捕獲任何丟擲的錯誤。
      2. 如果 func 執行成功並返回一個 Promise,那麼使用 catch 方法捕獲該 Promise 可能丟擲的錯誤,並應用 _guard 函式。
      3. 如果 func 執行成功並沒有返回 Promise,那麼直接返回結果。
      4. 如果 func 丟擲錯誤,應用 _guard 函式來決定是否重新丟擲錯誤或返回 undefined
      5. 如果提供了 shouldGuard 函式,它將用來判斷是否應該 "guard"(捕獲並返回 undefined)錯誤。如果 shouldGuard 函式返回 false,則丟擲原始錯誤;如果返回 true 或未提供 shouldGuard 函式,則返回 undefined

map:對一個陣列中的每個元素執行一個非同步對映函式

  1. 使用說明
    1. 功能描述:它用於對一個陣列中的每個元素執行一個非同步對映函式,並返回一個包含所有對映結果的新陣列。這個函式是 Array.prototype.map 方法的非同步版本。
    2. 引數:陣列,非同步函式。
    3. 返回值:對映後的新陣列。
  2. 使用程式碼示例
    import { map } from 'radash'
    
    const userIds = [1, 2, 3, 4]
    
    const users = await map(userIds, async (userId) => {
      return await api.users.find(userId)
    })
    
  3. 原始碼解析
    // 定義一個非同步函式 `map`。
    export const map = async <T, K>(
      // 第一個引數 `array` 是一個具有隻讀屬性的泛型陣列。
      array: readonly T[],
      // 第二個引數 `asyncMapFunc` 是一個非同步對映函式,它接受一個陣列元素和它的索引,
      // 返回一個 `Promise`,該 `Promise` 解析為新型別 `K` 的值。
      asyncMapFunc: (item: T, index: number) => Promise<K>
    ): Promise<K[]> => {
      // 如果傳入的陣列 `array` 不存在,則返回一個空陣列。
      if (!array) return []
      // 初始化一個空陣列 `result`,用於存放對映後的新值。
      let result = []
      // 初始化一個索引計數器 `index`。
      let index = 0
      // 使用 `for...of` 迴圈遍歷陣列 `array` 的每個元素。
      for (const value of array) {
        // 對每個元素呼叫 `asyncMapFunc` 對映函式,並等待其 `Promise` 解析。
        const newValue = await asyncMapFunc(value, index++)
        // 將解析後的新值新增到 `result` 陣列中。
        result.push(newValue)
      }
      // 迴圈完成後,返回包含所有新值的陣列 `result`。
      return result
    }
    
    • 方法流程說明:
      1. 檢查 array 是否存在。如果不存在,返回一個空陣列。
      2. 初始化一個空陣列 result 用於儲存對映結果,以及一個索引計數器 index
      3. 遍歷 array 中的每個元素。
      4. 對於每個元素,呼叫非同步對映函式 asyncMapFunc 並等待 Promise 解析。
      5. 將非同步對映函式解析後的結果新增到 result 陣列中。
      6. 在遍歷完所有元素之後,返回包含所有對映結果的 result 陣列。

parallel:並行地處理陣列中的元素,並對每個元素執行一個非同步函式

  1. 使用說明
    1. 功能描述:這個函式會限制同時進行的非同步操作的數量,以避免同時啟動過多的非同步任務。
    2. 引數:限制數量(number)、需要被非同步處理的元素陣列、轉換函式(將陣列中的每個元素轉換為一個非同步操作)。
    3. 返回值:返回一個陣列,該陣列包含了按原陣列順序排序的所有成功的結果。
  2. 使用程式碼示例
    // 定義一個非同步泛型函式 `parallel`。
    export const parallel = async <T, K>(
      // `limit` 是一個數字,指定了可以同時執行的非同步任務的最大數量。
      limit: number,
      // `array` 是一個只讀陣列,包含將要被非同步處理的元素。
      array: readonly T[],
      // `func` 是一個函式,將陣列中的每個元素轉換為一個非同步操作(返回 Promise)。
      func: (item: T) => Promise<K>
    ): Promise<K[]> => {
      // 將陣列 `array` 轉換為包含元素和它們索引的物件的陣列 `work`。
      const work = array.map((item, index) => ({
        index,
        item
      }))
      // 定義一個處理函式 `processor`,它將非同步處理 `work` 陣列中的元素。
      const processor = async (res: (value: WorkItemResult<K>[]) => void) => {
        const results: WorkItemResult<K>[] = []
        while (true) {
          // 從 `work` 陣列的末尾取出一個元素進行處理。
          const next = work.pop()
          // 如果沒有更多元素,呼叫回撥函式 `res` 並傳入結果陣列 `results`。
          if (!next) return res(results)
          // 使用 `tryit` 函式執行 `func` 並處理結果或錯誤。
          const [error, result] = await tryit(func)(next.item)
          // 將結果或錯誤新增到 `results` 陣列中。
          results.push({
            error,
            result: result as K,
            index: next.index
          })
        }
      }
      // 建立一個 `queues` 陣列,它包含了 `limit` 個新的 Promise,每個 Promise 都由 `processor` 函式處理。
      const queues = list(1, limit).map(() => new Promise(processor))
      // 使用 `Promise.all` 等待所有的 `queues` 中的 Promise 完成。
      const itemResults = (await Promise.all(queues)) as WorkItemResult<K>[][]
      // 將所有的結果扁平化並根據索引排序,然後使用 `fork` 函式將結果分為錯誤和成功的結果。
      const [errors, results] = fork(
        sort(itemResults.flat(), r => r.index),
        x => !!x.error
      )
      // 如果有任何錯誤,丟擲一個 `AggregateError` 包含所有錯誤。
      if (errors.length > 0) {
        throw new AggregateError(errors.map(error => error.error))
      }
      // 返回一個陣列,它包含了按原陣列順序排序的所有成功的結果。
      return results.map(r => r.result)
    }
    
  3. 原始碼解析
    // 定義一個非同步泛型函式 `parallel`。
    export const parallel = async <T, K>(
      // `limit` 是一個數字,指定了可以同時執行的非同步任務的最大數量。
      limit: number,
      // `array` 是一個只讀陣列,包含將要被非同步處理的元素。
      array: readonly T[],
      // `func` 是一個函式,將陣列中的每個元素轉換為一個非同步操作(返回 Promise)。
      func: (item: T) => Promise<K>
    ): Promise<K[]> => {
      // 將陣列 `array` 轉換為包含元素和它們索引的物件的陣列 `work`。
      const work = array.map((item, index) => ({
        index,
        item
      }))
      // 定義一個處理函式 `processor`,它將非同步處理 `work` 陣列中的元素。
      const processor = async (res: (value: WorkItemResult<K>[]) => void) => {
        const results: WorkItemResult<K>[] = []
        while (true) {
          // 從 `work` 陣列的末尾取出一個元素進行處理。
          const next = work.pop()
          // 如果沒有更多元素,呼叫回撥函式 `res` 並傳入結果陣列 `results`。
          if (!next) return res(results)
          // 使用 `tryit` 函式執行 `func` 並處理結果或錯誤。
          const [error, result] = await tryit(func)(next.item)
          // 將結果或錯誤新增到 `results` 陣列中。
          results.push({
            error,
            result: result as K,
            index: next.index
          })
        }
      }
      // 建立一個 `queues` 陣列,它包含了 `limit` 個新的 Promise,每個 Promise 都由 `processor` 函式處理。
      const queues = list(1, limit).map(() => new Promise(processor))
      // 使用 `Promise.all` 等待所有的 `queues` 中的 Promise 完成。
      const itemResults = (await Promise.all(queues)) as WorkItemResult<K>[][]
      // 將所有的結果扁平化並根據索引排序,然後使用 `fork` 函式將結果分為錯誤和成功的結果。
      const [errors, results] = fork(
        sort(itemResults.flat(), r => r.index),
        x => !!x.error
      )
      // 如果有任何錯誤,丟擲一個 `AggregateError` 包含所有錯誤。
      if (errors.length > 0) {
        throw new AggregateError(errors.map(error => error.error))
      }
      // 返回一個陣列,它包含了按原陣列順序排序的所有成功的結果。
      return results.map(r => r.result)
    }
    
    • 這段程式碼中使用了幾個未定義的函式和型別,如 tryitlistforksort,以及型別 WorkItemResult<K>。我們可以假設這些函式和型別具有以下功能:
      • tryit(func)(item):執行 func(item) 並捕獲任何丟擲的錯誤,返回一個包含錯誤和結果的元組。
      • list(1, limit):建立一個包含從 1 到 limit 的數字的陣列。
      • fork(array, condition):分割陣列 array,根據 condition 函式返回的布林值將陣列分為包含錯誤的元素和成功的元素兩個陣列。
      • sort(array, keySelector):根據 keySelector 函式返回的鍵對陣列 array 進行排序。
      • WorkItemResult<K>:一個型別,表示工作項的結果,包含可能的 error、成功的 result 以及元素的 index

reduce:對陣列中的每個元素執行一個非同步歸約函式

  1. 使用說明
    1. 功能描述:它是 Array.prototype.reduce 方法的非同步版本,用於對陣列中的每個元素執行一個非同步歸約函式,並返回最終的歸約值。
    2. 引數:被歸約處理的元素陣列、非同步歸約函式。
    3. 返回值:返回最終歸約的值。
  2. 使用程式碼示例
    import { reduce } from 'radash'
    
    const userIds = [1, 2, 3, 4]
    
    const users = await reduce(userIds, async (acc, userId) => {
      const user = await api.users.find(userId)
      return {
        ...acc,
        [userId]: user
      }
    }, {})
    
  3. 原始碼解析
    // 定義一個非同步泛型函式 `reduce`。
    export const reduce = async <T, K>(
      // 第一個引數 `array` 是一個只讀陣列,包含將要被歸約處理的元素。
      array: readonly T[],
      // 第二個引數 `asyncReducer` 是一個非同步歸約函式,它接受累加值 `acc`、當前元素 `item` 和它的索引 `index`,
      // 並返回一個 `Promise`,該 `Promise` 解析為新的累加值。
      asyncReducer: (acc: K, item: T, index: number) => Promise<K>,
      // 第三個引數 `initValue` 是可選的初始值。
      initValue?: K
    ): Promise<K> => {
      // 檢查初始值是否提供了。
      const initProvided = initValue !== undefined
      // 如果沒有提供初始值且陣列為空,則丟擲錯誤。
      if (!initProvided && array?.length < 1) {
        throw new Error('Cannot reduce empty array with no init value')
      }
      // 如果提供了初始值,使用整個陣列;否則,從陣列的第二個元素開始迭代。
      const iter = initProvided ? array : array.slice(1)
      // 初始化累加值 `value`。如果提供了初始值,使用它;否則使用陣列的第一個元素。
      let value: any = initProvided ? initValue : array[0]
      // 使用 `for...of` 迴圈和 `entries` 方法遍歷陣列或其子陣列。
      for (const [i, item] of iter.entries()) {
        // 對每個元素呼叫非同步歸約函式 `asyncReducer` 並等待其 `Promise` 解析。
        value = await asyncReducer(value, item, i)
      }
      // 迴圈完成後,返回最終的累加值 `value`。
      return value
    }
    
    • 方法流程說明:
      1. 檢查是否提供了初始值 initValue
      2. 如果沒有提供初始值且陣列為空,則丟擲錯誤,因為無法從空陣列中歸約出一個值。
      3. 確定迭代的陣列。如果提供了初始值,則迭代整個陣列;如果沒有提供初始值,則從陣列的第二個元素開始迭代。
      4. 初始化累加值 value。如果提供了初始值,則使用該初始值;如果沒有提供初始值,則使用陣列的第一個元素作為初始累加值。
      5. 遍歷陣列,對每個元素呼叫非同步歸約函式 asyncReducer,並等待其返回的 Promise 解析。
      6. 更新累加值 valueasyncReducer 返回的新值。
      7. 在遍歷完所有元素之後,返回最終的累加值。

retry:反覆嘗試執行一個非同步操作,直到達到設定上限

  1. 使用說明
    1. 功能描述:用於反覆嘗試執行一個非同步操作,直到成功或達到重試次數上限。如果操作失敗,可以選擇在重試之間設定延遲或使用退避函式(backoff)來計算延遲時間。
    2. 引數:條件物件options(包含:重複次數、延遲、退避函式)、失敗執行的非同步操作函式。
    3. 返回值:可能發揮undefined。
  2. 使用程式碼示例
    import { retry } from 'radash'
    
    await retry({}, api.users.list)
    await retry({ times: 10 }, api.users.list)
    await retry({ times: 2, delay: 1000 }, api.users.list)
    
    // exponential backoff
    await retry({ backoff: i => 10**i }, api.users.list)
    
  3. 原始碼解析
    // 定義一個非同步泛型函式 `retry`。
    export const retry = async <TResponse>(
      // `options` 物件包含重試策略的選項。
      options: {
        times?: number // 重試次數,預設為 3。
        delay?: number | null // 固定延遲時間,如果提供,則在重試之間等待這麼多毫秒。
        backoff?: (count: number) => number // 退避函式,可以根據重試次數來計算延遲時間。
      },
      // `func` 是要執行的非同步函式,它可能會失敗。
      func: (exit: (err: any) => void) => Promise<TResponse>
    ): Promise<TResponse> => {
      // 從 `options` 中獲取重試次數、固定延遲時間和退避函式。
      const times = options?.times ?? 3
      const delay = options?.delay
      const backoff = options?.backoff ?? null
    
      // 使用 `range` 函式生成一個序列,並遍歷這個序列進行重試。
      for (const i of range(1, times)) {
        // 嘗試執行 `func` 函式,並捕獲可能的錯誤 `err` 和結果 `result`。
        const [err, result] = (await tryit(func)((err: any) => {
          // 如果 `func` 失敗,並使用 `exit` 函式退出,則丟擲一個特殊的錯誤物件。
          throw { _exited: err }
        })) as [any, TResponse]
    
        // 如果沒有錯誤,說明 `func` 成功執行,返回結果。
        if (!err) return result
    
        // 如果有特殊的退出錯誤,重新丟擲原始錯誤。
        if (err._exited) throw err._exited
    
        // 如果是最後一次重試且仍然失敗,丟擲錯誤。
        if (i === times) throw err
    
        // 如果設定了固定延遲時間,使用 `sleep` 函式等待。
        if (delay) await sleep(delay)
    
        // 如果提供了退避函式,根據重試次數計算延遲時間並等待。
        if (backoff) await sleep(backoff(i))
      }
    
      // 如果程式碼執行到這裡,說明邏輯上不應該到達的程式碼路徑。
      // 這是為了滿足 TypeScript 的嚴格模式要求。
      /* istanbul ignore next */
      return undefined as unknown as TResponse
    }
    
    • 方法流程說明:
      1. options 中獲取重試次數、延遲和退避函式。
      2. 遍歷從 1 到重試次數的範圍。
      3. 在每次迭代中,嘗試執行 func 並捕獲可能的錯誤和結果。
      4. 如果 func 成功執行(沒有錯誤),返回結果。
      5. 如果有錯誤,並且是透過 exit 函式顯式退出的,重新丟擲原始錯誤。
      6. 如果達到了重試次數上限並且仍然失敗,丟擲最後一次的錯誤。
      7. 如果指定了延遲或退避函式,根據相應的策略等待一段時間後再重試。
      8. 如果執行到函式的末尾,返回 undefined 作為佔位符,因為邏輯上不應該到達這裡。

sleep:提供一個延時機制

  1. 使用說明
    1. 功能描述:提供一個延時機制,通常用於非同步操作中的暫停。
    2. 引數:暫停時間(ms)。
    3. 返回值:返回一個新的Promise。
  2. 使用程式碼示例
    import { sleep } from 'radash'
    
    await sleep(2000) // => waits 2 seconds
    
  3. 原始碼解析
    // 定義一個名為 `sleep` 的函式。
    export const sleep = (milliseconds: number) => {
      // 返回一個新的 Promise。
      return new Promise(res => 
        // 使用 `setTimeout` 函式設定一個定時器,它在 `milliseconds` 指定的毫秒數後執行。
        setTimeout(
          // 當定時器到時,呼叫 `res` 函式來解析這個 Promise。
          res,
          // 傳遞給 `setTimeout` 的毫秒數,它決定了延時的長度。
          milliseconds
        )
      )
    }
    
    • 方法流程說明:當你呼叫 sleep 函式並傳入一個毫秒數時,它會返回一個 Promise。這個 Promise 不會立即解析,而是會等待你指定的時間長度。當時間到了之後,Promise 會被解析,然後你可以在 .then() 方法中繼續執行後續的程式碼,或者你可以在 async 函式中使用 await 關鍵字來等待 Promise 解析。

tryit:捕獲函式在執行過程中可能丟擲的同步或非同步錯誤

  1. 使用說明
    1. 功能描述:tryit 是一個高階函式。用於捕獲函式在執行過程中可能丟擲的同步或非同步錯誤,並返回一個元組,其中包含錯誤物件或函式的返回值。這個函式的目的是提供一種安全執行任意函式並處理錯誤的方式。
    2. 引數:需要被捕獲的函式。
    3. 返回值:返回一個新函式,該函式接收與傳入函式相同的引數。
  2. 使用程式碼示例
    import { tryit } from 'radash'
    
    const findUser = tryit(api.users.find)
    
    const [err, user] = await findUser(userId)
    
  3. 原始碼解析
    // 定義一個泛型高階函式 `tryit`。
    export const tryit = <Args extends any[], Return>(
      // `func` 是一個接受任意引數的函式,其返回值可以是任何型別,包括 `Promise`。
      func: (...args: Args) => Return
    ) => {
      // 返回一個新函式,這個新函式接受與 `func` 相同的引數。
      return (
        ...args: Args
      // 新函式的返回型別取決於 `func` 的返回型別是否是 `Promise`。
      // 如果 `func` 返回 `Promise`,則返回一個 `Promise`,包含一個錯誤或函式返回值的元組。
      // 如果 `func` 返回非 `Promise`,則直接返回錯誤或函式返回值的元組。
      ): Return extends Promise<any>
        ? Promise<[Error, undefined] | [undefined, Awaited<Return>]>
        : [Error, undefined] | [undefined, Return] => {
        try {
          // 嘗試執行 `func` 並獲取結果。
          const result = func(...args)
          // 使用輔助函式 `isPromise` 檢查 `result` 是否是 `Promise`。
          if (isPromise(result)) {
            // 如果是 `Promise`,使用 `then` 和 `catch` 方法處理結果或捕獲錯誤。
            return result
              .then(value => [undefined, value]) // 成功時返回值的元組。
              .catch(err => [err, undefined]) // 錯誤時返回錯誤的元組。
          }
          // 如果 `result` 不是 `Promise`,直接返回值的元組。
          return [undefined, result]
        } catch (err) {
          // 如果執行 `func` 時捕獲到同步錯誤,返回錯誤的元組。
          return [err as any, undefined]
        }
      }
    }
    
    • 方法流程說明:
      1. tryit 函式接受一個函式 func 作為引數。
      2. tryit 返回一個新函式,這個新函式接受與 func 相同的引數。
      3. 當呼叫這個新函式時,它嘗試執行 func
      4. 如果 func 成功執行,且其返回值不是 Promise,新函式返回一個元組 [undefined, result]
      5. 如果 func 返回一個 Promise,新函式返回一個 Promise,該 Promise 解析為元組 [undefined, value][err, undefined],具體取決於 Promise 是成功解析還是被拒絕。
      6. 如果在執行 func 時捕獲到同步錯誤,新函式返回一個元組 [err, undefined]
      7. 如果 func 的返回型別是 Promise,那麼新函式的返回型別也是 Promise,否則返回型別就是元組。

寫在後面

  • 後續我們會繼續分享 Radash 庫中其他方法的使用和原始碼解析。
  • 大家有任何問題或見解,歡迎評論區留言交流和批評指正!!!
  • 你的每一個點贊和收藏都是作者寫作的動力!!!
  • 點選訪問:radash官網

相關文章