點燈坊

失くすものさえない今が強くなるチャンスよ

使用 all() 等 Array 中所有 Promise 都 Fulfilled

Sam Xiao's Avatar 2020-04-05

Promise.all() 對處理 Promise Array 非常好用,讓我們不必使用 Imperative 的 for await of,但可惜 Promise.all() 不適合 Pipeline,Wink-fp 特別提供 all() 取代 Promise.all()

Version

macOS Catalina 10.15.4
VS Code 1.43.2
Quokka 1.0.285
Ramda 0.27.0
Wink-fp 1.20.67

Imperative

let data = [
  Promise.resolve(1),
  Promise.resolve(2)
]

let f = arr => {
  for (let x of arr)
    console.log(x)
}

f(data)

第 1 行

let data = [
  Promise.resolve(1),
  Promise.resolve(2)
]

data 為 array,但其 element 都是 promise。

第 6 行

let f = arr => {
  for (let x of arr)
    console.log(x)
}

若想印出 array 中的值,直覺會使用 for loop 搭配 console.log()

all000

await

let data = [
  Promise.resolve(1),
  Promise.resolve(2)
]

let f = async arr => {
  for (let x of arr)
    console.log(await x)
}

f(data)

第 6 行

let f = async arr => {
  for (let x of arr)
    console.log(await x)
}

若要 console.log() 印出 promise 內部值,必須在每個 x 前加上 await 從 promise 取出。

all001

for await of

let data = [
  Promise.resolve(1),
  Promise.resolve(2)
]

let f = async arr => {
  for await(let x of arr)
    console.log(x)
}

f(data)

ES2017 提供了 for await of loop,如此 x 已經從 promise 中取出,console.log() 可直接使用。

all002

Promise.all()

let data = [
  Promise.resolve(1),
  Promise.resolve(2)
]

let f = arr => Promise.all(arr)
  .then(x => x.forEach(y => console.log(y)))

f(data)

若不想使用 async await,對於 promise array 可先使用 Promise.all() 處理,如此 promise array 中的值會先從 promise 取出,唯 promise.all() 回傳為新的 promise,因此再使用 then() 解開才能搭配 forEach()console.log() 印出。

all003

Ramda

import { andThen, pipe, forEach } from 'ramda'

let data = [
  Promise.resolve(1),
  Promise.resolve(2)
]

let f = pipe(
  x => Promise.all(x),
  andThen(forEach(console.log))
)

f(data)

若使用 Ramda,則可善其 andThen()forEach(),整個流程以 pipe() 整合起來。

為 Ramda 並沒有提供 Promise.all() 的 function 版本,因此只能自行使用 arrow function。

all004

Wink-fp

import { forEach } from 'ramda'
import { pipeP, all, resolve, log } from 'wink-fp'

let data = [
  resolve(1),
  resolve(2)
]

let f = pipeP(
  all,
  forEach(log)
)

f(data)

Wink-fp 提供了 Promise.all() 的 function 版本 all(),可直接搭配 pipeP() 使用。

resolve() 則為 Promise.resolve() 的 function 版本。

all()
[Promise e a] -> Promise e b
Promise.all() 的 pure function 版本

[Promise e a]:data 為 array promise

Promise e b:已經 fulfilled promise,但包了一層新 promise

all005

Conclusion

  • for await of loop 比原本 for of loop 寫法優雅,且 {} 可直接使用 x,不必再一直重複 await,但本質仍是 imperative
  • 若不喜歡使用 await,promise array 可先用 Promise.all() 處理,別忘了其回傳仍是 promise,因此必須接著使用 then() 才能取得 promise 內部值
  • 透過 Wink-fp 的 all(),則 Promise.all() 也能整合於 pipeP()