p-mapで配列の配列の非同期処理のPromiseのreject対応

配列の配列の非同期処理で対象API(以下ではsleep)がrejectする場合のメモ

# 前回

https://qiita.com/kuangzhixitian3/items/cd7cbfa27b2370987962

## 配列が１つの場合

### 全部同時実行

```plantuml
@startuml
robust "全体" as all
concise "https://test/1" as test1
concise "https://test/2" as test2
concise "https://test/3" as test3
concise "https://test/4" as test4
concise "https://test/5" as test5

@all
0 is 待機中
+1 is 処理中
+5 is 待機中

@test1
1 is 処理中
+1 is {hidden}

@test2
1 is 処理中 #pink
+2 is {hidden}

@test3
1 is 処理中 #pink
+0.01 is {hidden}

@test4
1 is 処理中 #pink
+4 is {hidden}

@test5
1 is 処理中
+5 is {hidden}

@enduml
```

```ts
import { describe, test } from 'vitest'
import pMap from 'p-map'

const sleep = (wait: number) =>
  new Promise((resolve, reject) => {
    const waitSeconds = wait / 1000
    if (waitSeconds % 3 === 0) {
      reject('Sleep NG before wait')
    } else if (waitSeconds % 2 === 0) {
      setTimeout(() => reject(`${wait} - Sleep NG after wait`), wait)
    } else {
      setTimeout(() => resolve(wait), wait)
    }
  })

function checkLog(text: string) {
  console.log(`time : ${new Date().toISOString()} - text: ${text}`)
}

const testOptions = {
  timeout: 1000 * 100
}

const mapper1 = async (site: string) => {
  const index = site.slice(-1)
  const wait = Number(index) * 1000
  try {
    const _wait = await sleep(wait)
    checkLog(site)
    return `${site} - ok - ${_wait}`
  } catch (error) {
    checkLog(`${site} - ng - ${error}`)
    return `${site} - ng - ${error}`
  }
}

describe('p-map 非同期処理１つの配列', () => {
  test('使い方１ー全部同時実行', testOptions, async () => {
    const sites = [
      'https://test/1',
      'https://test/2',
      'https://test/3',
      'https://test/4',
      'https://test/5'
    ]

    checkLog('[start]')

    const result = await pMap(sites, mapper1)

    console.log(result)
  })
})
```

実行結果
```ts
time : 2025-01-19T03:00:53.743Z - text: [start]
time : 2025-01-19T03:00:53.745Z - text: https://test/3 - ng - Sleep NG before wait
time : 2025-01-19T03:00:54.745Z - text: https://test/1
time : 2025-01-19T03:00:55.747Z - text: https://test/2 - ng - 2000 - Sleep NG after wait
time : 2025-01-19T03:00:57.761Z - text: https://test/4 - ng - 4000 - Sleep NG after wait
time : 2025-01-19T03:00:58.746Z - text: https://test/5
[
  'https://test/1 - ok - 1000',
  'https://test/2 - ng - 2000 - Sleep NG after wait',
  'https://test/3 - ng - Sleep NG before wait',
  'https://test/4 - ng - 4000 - Sleep NG after wait',
  'https://test/5 - ok - 5000'
]
```


### 最大２個まで同時実行

```plantuml
@startuml
robust "全体" as all
concise "https://test/1" as test1
concise "https://test/2" as test2
concise "https://test/3" as test3
concise "https://test/4" as test4
concise "https://test/5" as test5

@all
0 is 待機中
+1 is 処理中
+7 is 待機中

@test1
1 is 処理中
+1 is {hidden}

@test2
1 is 処理中 #pink
+2 is {hidden}

@test3
2 is 処理中 #pink
+0.01 is {hidden}

@test4
2 is 処理中 #pink
+4 is {hidden}

@test5
3 is 処理中
+5 is {hidden}

@enduml
```

```ts
import { describe, test } from 'vitest'
import pMap from 'p-map'

const sleep = (wait: number) =>
  new Promise((resolve, reject) => {
    const waitSeconds = wait / 1000
    if (waitSeconds % 3 === 0) {
      reject('Sleep NG before wait')
    } else if (waitSeconds % 2 === 0) {
      setTimeout(() => reject(`${wait} - Sleep NG after wait`), wait)
    } else {
      setTimeout(() => resolve(wait), wait)
    }
  })

function checkLog(text: string) {
  console.log(`time : ${new Date().toISOString()} - text: ${text}`)
}

const testOptions = {
  timeout: 1000 * 100
}

const mapper1 = async (site: string) => {
  const index = site.slice(-1)
  const wait = Number(index) * 1000
  try {
    const _wait = await sleep(wait)
    checkLog(site)
    return `${site} - ok - ${_wait}`
  } catch (error) {
    checkLog(`${site} - ng - ${error}`)
    return `${site} - ng - ${error}`
  }
}

describe('p-map 非同期処理１つの配列', () => {
  test('使い方２ー最大２個まで同時実行', testOptions, async () => {
    const sites = [
      'https://test/1',
      'https://test/2',
      'https://test/3',
      'https://test/4',
      'https://test/5'
    ]

    const concurrency = 2

    checkLog('[start]')

    const result = await pMap(sites, mapper1, { concurrency })

    console.log(result)
  })
})
```

実行結果
```ts
time : 2025-01-19T03:02:47.530Z - text: [start]
time : 2025-01-19T03:02:48.537Z - text: https://test/1
time : 2025-01-19T03:02:48.537Z - text: https://test/3 - ng - Sleep NG before wait
time : 2025-01-19T03:02:49.546Z - text: https://test/2 - ng - 2000 - Sleep NG after wait
time : 2025-01-19T03:02:52.540Z - text: https://test/4 - ng - 4000 - Sleep NG after wait
time : 2025-01-19T03:02:54.549Z - text: https://test/5
[
  'https://test/1 - ok - 1000',
  'https://test/2 - ng - 2000 - Sleep NG after wait',
  'https://test/3 - ng - Sleep NG before wait',
  'https://test/4 - ng - 4000 - Sleep NG after wait',
  'https://test/5 - ok - 5000'
]
```


## 配列が２つの場合（シンプル）

### 全部同時実行からの全部同時実行

```plantuml
@startuml
robust "全体" as all
concise "https://test/5" as test5
concise "https://test/5/1" as test5_1
concise "https://test/5/2" as test5_2
concise "https://test/5/3" as test5_3
concise "https://test/5/4" as test5_4
concise "https://test/5/5" as test5_5

@all
0 is 待機中
+1 is 処理中
+10 is 待機中

@test5
1 is 処理中
+5 is {hidden}

@test5_1
6 is 処理中
+1 is {hidden}

@test5_2
6 is 処理中 #pink
+2 is {hidden}

@test5_3
6 is 処理中 #pink
+0.01 is {hidden}

@test5_4
6 is 処理中 #pink
+4 is {hidden}

@test5_5
6 is 処理中
+5 is {hidden}

@enduml
```

```ts
import { describe, test } from 'vitest'
import pMap from 'p-map'

const sleep = (wait: number) =>
  new Promise((resolve, reject) => {
    const waitSeconds = wait / 1000
    if (waitSeconds % 3 === 0) {
      reject('Sleep NG before wait')
    } else if (waitSeconds % 2 === 0) {
      setTimeout(() => reject(`${wait} - Sleep NG after wait`), wait)
    } else {
      setTimeout(() => resolve(wait), wait)
    }
  })

function checkLog(text: string) {
  console.log(`time : ${new Date().toISOString()} - text: ${text}`)
}

const testOptions = {
  timeout: 1000 * 100
}

const mapper1 = async (site: string) => {
  const index = site.slice(-1)
  const wait = Number(index) * 1000
  try {
    const _wait = await sleep(wait)
    checkLog(site)
    return `${site} - ok - ${_wait}`
  } catch (error) {
    checkLog(`${site} - ng - ${error}`)
    return `${site} - ng - ${error}`
  }
}
function getWaitFromMapper1Message(mapper1Message: string) {
  const mapper1OkWaitString = mapper1Message.substring(mapper1Message.indexOf('ok') + 5)
  const mapper1OkWait = Number(mapper1OkWaitString)
  if (isNaN(mapper1OkWait)) {
    const mapper1NgWaitString = mapper1Message.substring(mapper1Message.indexOf('ng') + 5)
    const mapper1NgWaitStringWithoutErrorMessage = mapper1NgWaitString.substring(
      0,
      mapper1NgWaitString.indexOf('-')
    )
    const mapper1NgWait = Number(mapper1NgWaitStringWithoutErrorMessage)
    return isNaN(mapper1NgWait) ? 0 : mapper1NgWait
  } else {
    return mapper1OkWait
  }
}

const mapper2_max = async (sites: string[]) => {
  const site = sites[0]
  const index = site.substring(site.length - 3, site.length - 2)
  const wait = Number(index) * 1000
  try {
    const _wait = (await sleep(wait)) as number
    checkLog(`mapper2-index = ${index}`)

    const result = await pMap(sites, mapper1)
    const waitResults = result.map(getWaitFromMapper1Message)
    const maxWait = Math.max(...waitResults)
    return `${site.substring(0, site.length - 2)} - ok - ${_wait + maxWait}`
  } catch (error) {
    checkLog(`mapper2-index = ${index} - ng - ${error}`)
    return `${site.substring(0, site.length - 2)} - ng - ${error}`
  }
}

describe('p-map 非同期処理２つの配列', () => {
  test('使い方１ー全部同時実行からの全部同時実行', testOptions, async () => {
    const sites = [
      // ['https://test/1/1'],
      // ['https://test/2/1', 'https://test/2/2']
      //   ['https://test/3/1', 'https://test/3/2', 'https://test/3/3']
      // ['https://test/4/1', 'https://test/4/2', 'https://test/4/3', 'https://test/4/4'],
      [
        'https://test/5/1',
        'https://test/5/2',
        'https://test/5/3',
        'https://test/5/4',
        'https://test/5/5'
      ]
    ]

    checkLog('[start]')

    const result = await pMap(sites, mapper2_max)

    console.log(result)
  })
})
```

実行結果
```ts
time : 2025-01-19T03:06:15.325Z - text: [start]
time : 2025-01-19T03:06:20.329Z - text: mapper2-index = 5
time : 2025-01-19T03:06:20.331Z - text: https://test/5/3 - ng - Sleep NG before wait
time : 2025-01-19T03:06:21.345Z - text: https://test/5/1
time : 2025-01-19T03:06:22.333Z - text: https://test/5/2 - ng - 2000 - Sleep NG after wait
time : 2025-01-19T03:06:24.335Z - text: https://test/5/4 - ng - 4000 - Sleep NG after wait
time : 2025-01-19T03:06:25.342Z - text: https://test/5/5
[ 'https://test/5 - ok - 10000' ]
```


### 全部同時実行からの直列実行

```plantuml
@startuml
robust "全体" as all
concise "https://test/5" as test5
concise "https://test/5/1" as test5_1
concise "https://test/5/2" as test5_2
concise "https://test/5/3" as test5_3
concise "https://test/5/4" as test5_4
concise "https://test/5/5" as test5_5

@all
0 is 待機中
+1 is 処理中
+17 is 待機中

@test5
1 is 処理中
+5 is {hidden}

@test5_1
6 is 処理中
+1 is {hidden}

@test5_2
7 is 処理中 #pink
+2 is {hidden}

@test5_3
9 is 処理中 #pink
+0.01 is {hidden}

@test5_4
9 is 処理中 #pink
+4 is {hidden}

@test5_5
13 is 処理中
+5 is {hidden}

@enduml
```

```ts
import { describe, test } from 'vitest'
import pMap from 'p-map'

const sleep = (wait: number) =>
  new Promise((resolve, reject) => {
    const waitSeconds = wait / 1000
    if (waitSeconds % 3 === 0) {
      reject('Sleep NG before wait')
    } else if (waitSeconds % 2 === 0) {
      setTimeout(() => reject(`${wait} - Sleep NG after wait`), wait)
    } else {
      setTimeout(() => resolve(wait), wait)
    }
  })

function checkLog(text: string) {
  console.log(`time : ${new Date().toISOString()} - text: ${text}`)
}

const testOptions = {
  timeout: 1000 * 100
}

function sum(a: number, b: number) {
  return a + b
}

const mapper1 = async (site: string) => {
  const index = site.slice(-1)
  const wait = Number(index) * 1000
  try {
    const _wait = await sleep(wait)
    checkLog(site)
    return `${site} - ok - ${_wait}`
  } catch (error) {
    checkLog(`${site} - ng - ${error}`)
    return `${site} - ng - ${error}`
  }
}
function getWaitFromMapper1Message(mapper1Message: string) {
  const mapper1OkWaitString = mapper1Message.substring(mapper1Message.indexOf('ok') + 5)
  const mapper1OkWait = Number(mapper1OkWaitString)
  if (isNaN(mapper1OkWait)) {
    const mapper1NgWaitString = mapper1Message.substring(mapper1Message.indexOf('ng') + 5)
    const mapper1NgWaitStringWithoutErrorMessage = mapper1NgWaitString.substring(
      0,
      mapper1NgWaitString.indexOf('-')
    )
    const mapper1NgWait = Number(mapper1NgWaitStringWithoutErrorMessage)
    return isNaN(mapper1NgWait) ? 0 : mapper1NgWait
  } else {
    return mapper1OkWait
  }
}

const mapper2_sum = async (sites: string[]) => {
  const site = sites[0]
  const index = site.substring(site.length - 3, site.length - 2)
  const wait = Number(index) * 1000
  try {
    const _wait = (await sleep(wait)) as number
    checkLog(`mapper2-index = ${index}`)

    const concurrency = 1
    const result = await pMap(sites, mapper1, { concurrency })
    const waitResults = result.map(getWaitFromMapper1Message)
    const sumWait = waitResults.reduce(sum, 0)
    return `${site.substring(0, site.length - 2)} - ${_wait + sumWait}`
  } catch (error) {
    checkLog(`mapper2-index = ${index} - ng - ${error}`)
    return `${site.substring(0, site.length - 2)} - ng - ${error}`
  }
}

describe('p-map 非同期処理２つの配列', () => {
  test('使い方３ー全部同時実行からの直列実行', testOptions, async () => {
    const sites = [
      // ['https://test/1/1'],
      // ['https://test/2/1', 'https://test/2/2']
      //   ['https://test/3/1', 'https://test/3/2', 'https://test/3/3']
      // ['https://test/4/1', 'https://test/4/2', 'https://test/4/3', 'https://test/4/4'],
      [
        'https://test/5/1',
        'https://test/5/2',
        'https://test/5/3',
        'https://test/5/4',
        'https://test/5/5'
      ]
    ]

    checkLog('[start]')

    const result = await pMap(sites, mapper2_sum)

    console.log(result)
  })
})
```

実行結果
```ts
time : 2025-01-19T03:11:24.804Z - text: [start]
time : 2025-01-19T03:11:29.817Z - text: mapper2-index = 5
time : 2025-01-19T03:11:30.819Z - text: https://test/5/1
time : 2025-01-19T03:11:32.826Z - text: https://test/5/2 - ng - 2000 - Sleep NG after wait
time : 2025-01-19T03:11:32.827Z - text: https://test/5/3 - ng - Sleep NG before wait
time : 2025-01-19T03:11:36.830Z - text: https://test/5/4 - ng - 4000 - Sleep NG after wait
time : 2025-01-19T03:11:41.847Z - text: https://test/5/5
[ 'https://test/5 - 17000' ]
```



### 全部同時実行からの最大２個まで同時実行

```plantuml
@startuml
robust "全体" as all
concise "https://test/5" as test5
concise "https://test/5/1" as test5_1
concise "https://test/5/2" as test5_2
concise "https://test/5/3" as test5_3
concise "https://test/5/4" as test5_4
concise "https://test/5/5" as test5_5

@all
0 is 待機中
+1 is 処理中
+12 is 待機中

@test5
1 is 処理中
+5 is {hidden}

@test5_1
6 is 処理中
+1 is {hidden}

@test5_2
6 is 処理中 #pink
+2 is {hidden}

@test5_3
7 is 処理中 #pink
+0.01 is {hidden}

@test5_4
7 is 処理中 #pink
+4 is {hidden}

@test5_5
8 is 処理中
+5 is {hidden}

@enduml
```

```ts
import { describe, test } from 'vitest'
import pMap from 'p-map'

const sleep = (wait: number) =>
  new Promise((resolve, reject) => {
    const waitSeconds = wait / 1000
    if (waitSeconds % 3 === 0) {
      reject('Sleep NG before wait')
    } else if (waitSeconds % 2 === 0) {
      setTimeout(() => reject(`${wait} - Sleep NG after wait`), wait)
    } else {
      setTimeout(() => resolve(wait), wait)
    }
  })

function checkLog(text: string) {
  console.log(`time : ${new Date().toISOString()} - text: ${text}`)
}

const testOptions = {
  timeout: 1000 * 100
}

function array_slice_2(acc: number[][], cur: number) {
  const topLengt = acc.length
  if (topLengt == 0) {
    acc.push([cur])
  } else if (acc[topLengt - 1].length == 2) {
    acc.push([cur])
  } else {
    acc[topLengt - 1].push(cur)
  }

  return acc
}

function array_sum_slice_2(acc: number[], cur: number[]) {
  const accLengt = acc.length
  if (accLengt == 0) {
    acc.push(...cur)
  } else {
    const curLength = cur.length
    const min = Math.min(acc[0], acc[1])
    const minIndex = acc[0] == min ? 0 : 1
    acc[minIndex] += cur[0]

    if (curLength == 2) {
      const min2 = Math.min(acc[0], acc[1])
      const min2Index = acc[0] == min2 ? 0 : 1
      acc[min2Index] += cur[1]
    }
  }

  return acc
}

const mapper1 = async (site: string) => {
  const index = site.slice(-1)
  const wait = Number(index) * 1000
  try {
    const _wait = await sleep(wait)
    checkLog(site)
    return `${site} - ok - ${_wait}`
  } catch (error) {
    checkLog(`${site} - ng - ${error}`)
    return `${site} - ng - ${error}`
  }
}
function getWaitFromMapper1Message(mapper1Message: string) {
  const mapper1OkWaitString = mapper1Message.substring(mapper1Message.indexOf('ok') + 5)
  const mapper1OkWait = Number(mapper1OkWaitString)
  if (isNaN(mapper1OkWait)) {
    const mapper1NgWaitString = mapper1Message.substring(mapper1Message.indexOf('ng') + 5)
    const mapper1NgWaitStringWithoutErrorMessage = mapper1NgWaitString.substring(
      0,
      mapper1NgWaitString.indexOf('-')
    )
    const mapper1NgWait = Number(mapper1NgWaitStringWithoutErrorMessage)
    return isNaN(mapper1NgWait) ? 0 : mapper1NgWait
  } else {
    return mapper1OkWait
  }
}

const mapper2_limit2 = async (sites: string[]) => {
  const site = sites[0]
  const index = site.substring(site.length - 3, site.length - 2)
  const wait = Number(index) * 1000
  try {
    const _wait = (await sleep(wait)) as number
    checkLog(`mapper2-index = ${index}`)

    const concurrency = 2
    const result = await pMap(sites, mapper1, { concurrency })
    const waitResults = result.map(getWaitFromMapper1Message)
    const waitResultsArray = waitResults.reduce(array_slice_2, [])
    const waitResultsArraySum = waitResultsArray.reduce(array_sum_slice_2, [])
    const sumWait = Math.max(...waitResultsArraySum)
    return `${site.substring(0, site.length - 2)} - ${_wait + sumWait}`
  } catch (error) {
    checkLog(`mapper2-index = ${index} - ng - ${error}`)
    return `${site.substring(0, site.length - 2)} - ng - ${error}`
  }
}

describe('p-map 非同期処理２つの配列', () => {
  test('使い方５ー全部同時実行からの最大２個まで同時実行', testOptions, async () => {
    const sites = [
      // ['https://test/1/1'],
      // ['https://test/2/1', 'https://test/2/2']
      //   ['https://test/3/1', 'https://test/3/2', 'https://test/3/3']
      // ['https://test/4/1', 'https://test/4/2', 'https://test/4/3', 'https://test/4/4'],
      [
        'https://test/5/1',
        'https://test/5/2',
        'https://test/5/3',
        'https://test/5/4',
        'https://test/5/5'
      ]
    ]

    checkLog('[start]')

    const result = await pMap(sites, mapper2_limit2)

    console.log(result)
  })
})
```

実行結果
```ts
time : 2025-01-19T03:16:50.357Z - text: [start]
time : 2025-01-19T03:16:55.372Z - text: mapper2-index = 5
time : 2025-01-19T03:16:56.375Z - text: https://test/5/1
time : 2025-01-19T03:16:56.375Z - text: https://test/5/3 - ng - Sleep NG before wait
time : 2025-01-19T03:16:57.377Z - text: https://test/5/2 - ng - 2000 - Sleep NG after wait
time : 2025-01-19T03:17:00.377Z - text: https://test/5/4 - ng - 4000 - Sleep NG after wait
time : 2025-01-19T03:17:02.387Z - text: https://test/5/5
[ 'https://test/5 - 12000' ]
```


## 配列が２つの場合（複雑）

### 全部同時実行からの全部同時実行

```plantuml
@startuml
robust "全体" as all

concise "https://test/1" as test1
concise "https://test/1/1" as test1_1

concise "https://test/2" as test2
concise "https://test/2/1" as test2_1
concise "https://test/2/2" as test2_2

concise "https://test/3" as test3
concise "https://test/3/1" as test3_1
concise "https://test/3/2" as test3_2
concise "https://test/3/3" as test3_3

concise "https://test/4" as test4
concise "https://test/4/1" as test4_1
concise "https://test/4/2" as test4_2
concise "https://test/4/3" as test4_3
concise "https://test/4/4" as test4_4

concise "https://test/5" as test5
concise "https://test/5/1" as test5_1
concise "https://test/5/2" as test5_2
concise "https://test/5/3" as test5_3
concise "https://test/5/4" as test5_4
concise "https://test/5/5" as test5_5

@all
0 is 待機中
+1 is 処理中
+10 is 待機中

@test1
1 is 処理中
+1 is {hidden}
@test1_1
2 is 処理中
+1 is {hidden}

@test2
1 is 処理中 #pink
+2 is {hidden}

@test3
1 is 処理中 #pink
+0.01 is {hidden}

@test4
1 is 処理中 #pink
+4 is {hidden}

@test5
1 is 処理中
+5 is {hidden}
@test5_1
6 is 処理中
+1 is {hidden}
@test5_2
6 is 処理中 #pink
+2 is {hidden}
@test5_3
6 is 処理中 #pink
+0.01 is {hidden}
@test5_4
6 is 処理中 #pink
+4 is {hidden}
@test5_5
6 is 処理中
+5 is {hidden}

@enduml
```

```ts
import { describe, test } from 'vitest'
import pMap from 'p-map'

const sleep = (wait: number) =>
  new Promise((resolve, reject) => {
    const waitSeconds = wait / 1000
    if (waitSeconds % 3 === 0) {
      reject('Sleep NG before wait')
    } else if (waitSeconds % 2 === 0) {
      setTimeout(() => reject(`${wait} - Sleep NG after wait`), wait)
    } else {
      setTimeout(() => resolve(wait), wait)
    }
  })

function checkLog(text: string) {
  console.log(`time : ${new Date().toISOString()} - text: ${text}`)
}

const testOptions = {
  timeout: 1000 * 100
}

const mapper1 = async (site: string) => {
  const index = site.slice(-1)
  const wait = Number(index) * 1000
  try {
    const _wait = await sleep(wait)
    checkLog(site)
    return `${site} - ok - ${_wait}`
  } catch (error) {
    checkLog(`${site} - ng - ${error}`)
    return `${site} - ng - ${error}`
  }
}
function getWaitFromMapper1Message(mapper1Message: string) {
  const mapper1OkWaitString = mapper1Message.substring(mapper1Message.indexOf('ok') + 5)
  const mapper1OkWait = Number(mapper1OkWaitString)
  if (isNaN(mapper1OkWait)) {
    const mapper1NgWaitString = mapper1Message.substring(mapper1Message.indexOf('ng') + 5)
    const mapper1NgWaitStringWithoutErrorMessage = mapper1NgWaitString.substring(
      0,
      mapper1NgWaitString.indexOf('-')
    )
    const mapper1NgWait = Number(mapper1NgWaitStringWithoutErrorMessage)
    return isNaN(mapper1NgWait) ? 0 : mapper1NgWait
  } else {
    return mapper1OkWait
  }
}

const mapper2_max = async (sites: string[]) => {
  const site = sites[0]
  const index = site.substring(site.length - 3, site.length - 2)
  const wait = Number(index) * 1000
  try {
    const _wait = (await sleep(wait)) as number
    checkLog(`mapper2-index = ${index}`)

    const result = await pMap(sites, mapper1)
    const waitResults = result.map(getWaitFromMapper1Message)
    const maxWait = Math.max(...waitResults)
    return `${site.substring(0, site.length - 2)} - ok - ${_wait + maxWait}`
  } catch (error) {
    checkLog(`mapper2-index = ${index} - ng - ${error}`)
    return `${site.substring(0, site.length - 2)} - ng - ${error}`
  }
}

describe('p-map 非同期処理２つの配列', () => {
  test('使い方２ー全部同時実行からの全部同時実行', testOptions, async () => {
    const sites = [
      ['https://test/1/1'],
      ['https://test/2/1', 'https://test/2/2'],
      ['https://test/3/1', 'https://test/3/2', 'https://test/3/3'],
      ['https://test/4/1', 'https://test/4/2', 'https://test/4/3', 'https://test/4/4'],
      [
        'https://test/5/1',
        'https://test/5/2',
        'https://test/5/3',
        'https://test/5/4',
        'https://test/5/5'
      ]
    ]

    checkLog('[start]')

    const result = await pMap(sites, mapper2_max)

    console.log(result)
  })
})
```

実行結果
```ts
time : 2025-01-19T03:28:05.628Z - text: [start]
time : 2025-01-19T03:28:05.630Z - text: mapper2-index = 3 - ng - Sleep NG before wait
time : 2025-01-19T03:28:06.636Z - text: mapper2-index = 1
time : 2025-01-19T03:28:07.637Z - text: mapper2-index = 2 - ng - 2000 - Sleep NG after wait
time : 2025-01-19T03:28:07.638Z - text: https://test/1/1
time : 2025-01-19T03:28:09.630Z - text: mapper2-index = 4 - ng - 4000 - Sleep NG after wait
time : 2025-01-19T03:28:10.640Z - text: mapper2-index = 5
time : 2025-01-19T03:28:10.641Z - text: https://test/5/3 - ng - Sleep NG before wait
time : 2025-01-19T03:28:11.657Z - text: https://test/5/1
time : 2025-01-19T03:28:12.653Z - text: https://test/5/2 - ng - 2000 - Sleep NG after wait
time : 2025-01-19T03:28:14.642Z - text: https://test/5/4 - ng - 4000 - Sleep NG after wait
time : 2025-01-19T03:28:15.643Z - text: https://test/5/5
[
  'https://test/1 - ok - 2000',
  'https://test/2 - ng - 2000 - Sleep NG after wait',
  'https://test/3 - ng - Sleep NG before wait',
  'https://test/4 - ng - 4000 - Sleep NG after wait',
  'https://test/5 - ok - 10000'
]
```


### 全部同時実行からの直列実行

```plantuml
@startuml
robust "全体" as all

concise "https://test/1" as test1
concise "https://test/1/1" as test1_1

concise "https://test/2" as test2
concise "https://test/2/1" as test2_1
concise "https://test/2/2" as test2_2

concise "https://test/3" as test3
concise "https://test/3/1" as test3_1
concise "https://test/3/2" as test3_2
concise "https://test/3/3" as test3_3

concise "https://test/4" as test4
concise "https://test/4/1" as test4_1
concise "https://test/4/2" as test4_2
concise "https://test/4/3" as test4_3
concise "https://test/4/4" as test4_4

concise "https://test/5" as test5
concise "https://test/5/1" as test5_1
concise "https://test/5/2" as test5_2
concise "https://test/5/3" as test5_3
concise "https://test/5/4" as test5_4
concise "https://test/5/5" as test5_5

@all
0 is 待機中
+1 is 処理中
+17 is 待機中

@test1
1 is 処理中
+1 is {hidden}
@test1_1
2 is 処理中
+1 is {hidden}

@test2
1 is 処理中 #pink
+2 is {hidden}

@test3
1 is 処理中 #pink
+0.01 is {hidden}

@test4
1 is 処理中 #pink
+4 is {hidden}

@test5
1 is 処理中
+5 is {hidden}
@test5_1
6 is 処理中
+1 is {hidden}
@test5_2
7 is 処理中 #pink
+2 is {hidden}
@test5_3
9 is 処理中 #pink
+0.01 is {hidden}
@test5_4
9 is 処理中 #pink
+4 is {hidden}
@test5_5
13 is 処理中
+5 is {hidden}

@enduml
```

```ts
import { describe, test } from 'vitest'
import pMap from 'p-map'

const sleep = (wait: number) =>
  new Promise((resolve, reject) => {
    const waitSeconds = wait / 1000
    if (waitSeconds % 3 === 0) {
      reject('Sleep NG before wait')
    } else if (waitSeconds % 2 === 0) {
      setTimeout(() => reject(`${wait} - Sleep NG after wait`), wait)
    } else {
      setTimeout(() => resolve(wait), wait)
    }
  })

function checkLog(text: string) {
  console.log(`time : ${new Date().toISOString()} - text: ${text}`)
}

const testOptions = {
  timeout: 1000 * 100
}

function sum(a: number, b: number) {
  return a + b
}

const mapper1 = async (site: string) => {
  const index = site.slice(-1)
  const wait = Number(index) * 1000
  try {
    const _wait = await sleep(wait)
    checkLog(site)
    return `${site} - ok - ${_wait}`
  } catch (error) {
    checkLog(`${site} - ng - ${error}`)
    return `${site} - ng - ${error}`
  }
}
function getWaitFromMapper1Message(mapper1Message: string) {
  const mapper1OkWaitString = mapper1Message.substring(mapper1Message.indexOf('ok') + 5)
  const mapper1OkWait = Number(mapper1OkWaitString)
  if (isNaN(mapper1OkWait)) {
    const mapper1NgWaitString = mapper1Message.substring(mapper1Message.indexOf('ng') + 5)
    const mapper1NgWaitStringWithoutErrorMessage = mapper1NgWaitString.substring(
      0,
      mapper1NgWaitString.indexOf('-')
    )
    const mapper1NgWait = Number(mapper1NgWaitStringWithoutErrorMessage)
    return isNaN(mapper1NgWait) ? 0 : mapper1NgWait
  } else {
    return mapper1OkWait
  }
}

const mapper2_sum = async (sites: string[]) => {
  const site = sites[0]
  const index = site.substring(site.length - 3, site.length - 2)
  const wait = Number(index) * 1000
  try {
    const _wait = (await sleep(wait)) as number
    checkLog(`mapper2-index = ${index}`)

    const concurrency = 1
    const result = await pMap(sites, mapper1, { concurrency })
    const waitResults = result.map(getWaitFromMapper1Message)
    const sumWait = waitResults.reduce(sum, 0)
    return `${site.substring(0, site.length - 2)} - ${_wait + sumWait}`
  } catch (error) {
    checkLog(`mapper2-index = ${index} - ng - ${error}`)
    return `${site.substring(0, site.length - 2)} - ng - ${error}`
  }
}

describe('p-map 非同期処理２つの配列', () => {
  test('使い方４ー全部同時実行からの直列実行', testOptions, async () => {
    const sites = [
      ['https://test/1/1'],
      ['https://test/2/1', 'https://test/2/2'],
      ['https://test/3/1', 'https://test/3/2', 'https://test/3/3'],
      ['https://test/4/1', 'https://test/4/2', 'https://test/4/3', 'https://test/4/4'],
      [
        'https://test/5/1',
        'https://test/5/2',
        'https://test/5/3',
        'https://test/5/4',
        'https://test/5/5'
      ]
    ]

    checkLog('[start]')

    const result = await pMap(sites, mapper2_sum)

    console.log(result)
  })
})
```

実行結果
```ts
time : 2025-01-19T03:30:02.221Z - text: [start]
time : 2025-01-19T03:30:02.222Z - text: mapper2-index = 3 - ng - Sleep NG before wait
time : 2025-01-19T03:30:03.227Z - text: mapper2-index = 1
time : 2025-01-19T03:30:04.226Z - text: mapper2-index = 2 - ng - 2000 - Sleep NG after wait
time : 2025-01-19T03:30:04.229Z - text: https://test/1/1
time : 2025-01-19T03:30:06.235Z - text: mapper2-index = 4 - ng - 4000 - Sleep NG after wait
time : 2025-01-19T03:30:07.238Z - text: mapper2-index = 5
time : 2025-01-19T03:30:08.247Z - text: https://test/5/1
time : 2025-01-19T03:30:10.259Z - text: https://test/5/2 - ng - 2000 - Sleep NG after wait
time : 2025-01-19T03:30:10.259Z - text: https://test/5/3 - ng - Sleep NG before wait
time : 2025-01-19T03:30:14.265Z - text: https://test/5/4 - ng - 4000 - Sleep NG after wait
time : 2025-01-19T03:30:19.277Z - text: https://test/5/5
[
  'https://test/1 - 2000',
  'https://test/2 - ng - 2000 - Sleep NG after wait',
  'https://test/3 - ng - Sleep NG before wait',
  'https://test/4 - ng - 4000 - Sleep NG after wait',
  'https://test/5 - 17000'
]
```



### 全部同時実行からの最大２個まで同時実行

```plantuml
@startuml
robust "全体" as all

concise "https://test/1" as test1
concise "https://test/1/1" as test1_1

concise "https://test/2" as test2
concise "https://test/2/1" as test2_1
concise "https://test/2/2" as test2_2

concise "https://test/3" as test3
concise "https://test/3/1" as test3_1
concise "https://test/3/2" as test3_2
concise "https://test/3/3" as test3_3

concise "https://test/4" as test4
concise "https://test/4/1" as test4_1
concise "https://test/4/2" as test4_2
concise "https://test/4/3" as test4_3
concise "https://test/4/4" as test4_4

concise "https://test/5" as test5
concise "https://test/5/1" as test5_1
concise "https://test/5/2" as test5_2
concise "https://test/5/3" as test5_3
concise "https://test/5/4" as test5_4
concise "https://test/5/5" as test5_5

@all
0 is 待機中
+1 is 処理中
+14 is 待機中

@test1
1 is 処理中
+1 is {hidden}
@test1_1
2 is 処理中
+1 is {hidden}

@test2
1 is 処理中 #pink
+2 is {hidden}

@test3
1 is 処理中 #pink
+0.01 is {hidden}

@test4
1 is 処理中 #pink
+4 is {hidden}

@test5
1 is 処理中
+5 is {hidden}
@test5_1
6 is 処理中
+1 is {hidden}
@test5_2
6 is 処理中 #pink
+2 is {hidden}
@test5_3
7 is 処理中 #pink
+0.01 is {hidden}
@test5_4
7 is 処理中 #pink
+4 is {hidden}
@test5_5
8 is 処理中
+5 is {hidden}

@enduml
```

```ts
import { describe, test } from 'vitest'
import pMap from 'p-map'

const sleep = (wait: number) =>
  new Promise((resolve, reject) => {
    const waitSeconds = wait / 1000
    if (waitSeconds % 3 === 0) {
      reject('Sleep NG before wait')
    } else if (waitSeconds % 2 === 0) {
      setTimeout(() => reject(`${wait} - Sleep NG after wait`), wait)
    } else {
      setTimeout(() => resolve(wait), wait)
    }
  })

function checkLog(text: string) {
  console.log(`time : ${new Date().toISOString()} - text: ${text}`)
}

const testOptions = {
  timeout: 1000 * 100
}

function array_slice_2(acc: number[][], cur: number) {
  const topLengt = acc.length
  if (topLengt == 0) {
    acc.push([cur])
  } else if (acc[topLengt - 1].length == 2) {
    acc.push([cur])
  } else {
    acc[topLengt - 1].push(cur)
  }

  return acc
}

function array_sum_slice_2(acc: number[], cur: number[]) {
  const accLengt = acc.length
  if (accLengt == 0) {
    acc.push(...cur)
  } else {
    const curLength = cur.length
    const min = Math.min(acc[0], acc[1])
    const minIndex = acc[0] == min ? 0 : 1
    acc[minIndex] += cur[0]

    if (curLength == 2) {
      const min2 = Math.min(acc[0], acc[1])
      const min2Index = acc[0] == min2 ? 0 : 1
      acc[min2Index] += cur[1]
    }
  }

  return acc
}

const mapper1 = async (site: string) => {
  const index = site.slice(-1)
  const wait = Number(index) * 1000
  try {
    const _wait = await sleep(wait)
    checkLog(site)
    return `${site} - ok - ${_wait}`
  } catch (error) {
    checkLog(`${site} - ng - ${error}`)
    return `${site} - ng - ${error}`
  }
}
function getWaitFromMapper1Message(mapper1Message: string) {
  const mapper1OkWaitString = mapper1Message.substring(mapper1Message.indexOf('ok') + 5)
  const mapper1OkWait = Number(mapper1OkWaitString)
  if (isNaN(mapper1OkWait)) {
    const mapper1NgWaitString = mapper1Message.substring(mapper1Message.indexOf('ng') + 5)
    const mapper1NgWaitStringWithoutErrorMessage = mapper1NgWaitString.substring(
      0,
      mapper1NgWaitString.indexOf('-')
    )
    const mapper1NgWait = Number(mapper1NgWaitStringWithoutErrorMessage)
    return isNaN(mapper1NgWait) ? 0 : mapper1NgWait
  } else {
    return mapper1OkWait
  }
}

const mapper2_limit2 = async (sites: string[]) => {
  const site = sites[0]
  const index = site.substring(site.length - 3, site.length - 2)
  const wait = Number(index) * 1000
  try {
    const _wait = (await sleep(wait)) as number
    checkLog(`mapper2-index = ${index}`)

    const concurrency = 2
    const result = await pMap(sites, mapper1, { concurrency })
    const waitResults = result.map(getWaitFromMapper1Message)
    const waitResultsArray = waitResults.reduce(array_slice_2, [])
    const waitResultsArraySum = waitResultsArray.reduce(array_sum_slice_2, [])
    const sumWait = Math.max(...waitResultsArraySum)
    return `${site.substring(0, site.length - 2)} - ${_wait + sumWait}`
  } catch (error) {
    checkLog(`mapper2-index = ${index} - ng - ${error}`)
    return `${site.substring(0, site.length - 2)} - ng - ${error}`
  }
}

describe('p-map 非同期処理２つの配列', () => {
  test('使い方６ー全部同時実行からの最大２個まで同時実行', testOptions, async () => {
    const sites = [
      ['https://test/1/1'],
      ['https://test/2/1', 'https://test/2/2'],
      ['https://test/3/1', 'https://test/3/2', 'https://test/3/3'],
      ['https://test/4/1', 'https://test/4/2', 'https://test/4/3', 'https://test/4/4'],
      [
        'https://test/5/1',
        'https://test/5/2',
        'https://test/5/3',
        'https://test/5/4',
        'https://test/5/5'
      ]
    ]

    checkLog('[start]')

    const result = await pMap(sites, mapper2_limit2)

    console.log(result)
  })
})
```

実行結果
```ts
time : 2025-01-19T03:34:07.009Z - text: [start]
time : 2025-01-19T03:34:07.010Z - text: mapper2-index = 3 - ng - Sleep NG before wait
time : 2025-01-19T03:34:08.026Z - text: mapper2-index = 1
time : 2025-01-19T03:34:09.015Z - text: mapper2-index = 2 - ng - 2000 - Sleep NG after wait
time : 2025-01-19T03:34:09.031Z - text: https://test/1/1
time : 2025-01-19T03:34:11.022Z - text: mapper2-index = 4 - ng - 4000 - Sleep NG after wait
time : 2025-01-19T03:34:12.021Z - text: mapper2-index = 5
time : 2025-01-19T03:34:13.021Z - text: https://test/5/1
time : 2025-01-19T03:34:13.022Z - text: https://test/5/3 - ng - Sleep NG before wait
time : 2025-01-19T03:34:14.022Z - text: https://test/5/2 - ng - 2000 - Sleep NG after wait
time : 2025-01-19T03:34:17.031Z - text: https://test/5/4 - ng - 4000 - Sleep NG after wait
time : 2025-01-19T03:34:19.038Z - text: https://test/5/5
[
  'https://test/1 - 2000',
  'https://test/2 - ng - 2000 - Sleep NG after wait',
  'https://test/3 - ng - Sleep NG before wait',
  'https://test/4 - ng - 4000 - Sleep NG after wait',
  'https://test/5 - 12000'
]
```