點燈坊

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

在 Promise Chain 使用 Comma Operator

Sam Xiao's Avatar 2020-12-22

&&|| 為 ECMAScript 很具代表性的 Operator,事實上 ,&&|| 非常類似,尤其在 Promise Chain 中非常實用。

Version

ECMAScript 5

Synchronous

let y = 1
let x = y

x = x + 1
let z = x + y

z // ?

重點在 z 必須先執行 x = x + 1,然後再 x + y,一共需要兩行。

comma000

let x = y = 1

let z = (x++, x + y)

z // ?

可將 x++x + y 使用 comma operator 合併成一行。

comma001

Asynchronous

let x = Promise.resolve(1)
let y = Promise.resolve(1)

let x_ = await x + 1
let z = x_ + await y

await z // ?

xy 為 Promise,則要將原本 synchronous 都加上 await

comma002

let x = Promise.resolve(1)
let y = Promise.resolve(1)

let z = x
  .then(x => x + 1)
  .then(async x => x + await y)

await z // ?

也可改用 Promise Chain,由於第二個 .then() 又相依 Promise,因此在 callback 內直接使用 Async Await 較為精簡。

comma003

let x = Promise.resolve(1)
let y = Promise.resolve(1)

let z = x
  .then(async x => (x++, x + await y))

await z // ?

可使用 comma operator 將兩行合併成一行。

comma004

Promise Chain

comma005

Get Book 按下會同時顯示 FP in JavaScriptFP

<template>
  <div>
    <button @click="onClick">Get Book</button>
    {{ title }} / {{ category }}
  </div>
</template>

<script>
import axios from 'axios'

let fetchBook = x => axios.get(`http://localhost:3000/books/${x}`)
  .then(x => x.data)

let fetchCategory = x => axios.get(`http://localhost:3000/categories/${x}`)
  .then(x => x.data)

let onClick = function() {
  fetchBook(1)
    .then(x => (this.title = x.title, x))
    .then(x => fetchCategory(x.categoryId))
    .then(x => this.category = x.category)
    .catch(console.error)
}

export default {
  name:'App',
  data:() => ({
    title: '',
    category: '',
  }),
  methods:{
    onClick
  }
}
</script>

實務上 comma operator 真正有用是在 Promise Chain 處理上。

18 行

fetchBook(1)
  .then(x => (this.title = x.title, x))
  .then(x => fetchCategory(x.categoryId))
  .then(x => this.category = x.category)
  .catch(console.log)

第一個 .then()xbook,它有兩個任務:

  • 寫入 title side effect
  • 回傳 x 供下一個 API function 使用

傳統會寫成兩行,但透過 comma operator 只要一行即可。

Function Pipeline

<template>
  <div>
    <button @click="onClick">Get Book</button>
    {{ title }} / {{ category }}
  </div>
</template>

<script>
import { pipe, andThen as then, otherwise } from 'ramda'
import axios from 'axios'

let fetchBook = x => axios.get(`http://localhost:3000/books/${x}`)
  .then(x => x.data)

let fetchCategory = x => axios.get(`http://localhost:3000/categories/${x}`)
  .then(x => x.data)

let onClick = function() {  
  pipe(
    fetchBook,
    then(x => (this.title = x.title, x)),
    then(x => fetchCategory(x.categoryId)),
    then(x => this.category = x.category),
    otherwise(console.error)
  )(1)
}

export default {
  name:'App',
  data:() => ({
    title: '',
    category: '',
  }),
  methods:{
    onClick
  }
}
</script>

19 行

pipe(
  fetchBook,
  then(x => (this.title = x.title, x)),
  then(x => fetchCategory(x.categoryId)),
  then(x => this.category = x.category),
  otherwise(console.error)
)(1)

使用將 Method Chaining 改成 Function Pipeline。

Conclusion

  • Comma operator 過去較不受重視,但隨著 Promise Chain 流行後,我們常在 .then() 中只寫一行的 arrow function,此時可將 side effect 與 pure function 使用 comma operator 寫成一行

Reference

MDN, Comma Operator