點燈坊

學而時習之,不亦悅乎

實務上 then() 與 await 在 Vue 使用方式

Sam Xiao's Avatar 2019-09-12

ECMAScript 2015 提出了 Promise 後,提供了 then() 用法,而 ECMAScript 2017 又提出了 await 用法,但 Vue 該如何活用 then()await 呢 ?

Version

macOS Mojave 10.14.6
WebStorm 2019.2.1
Vue CLI 3.11.0
Vue 2.6.10
Ramda 0.26.1
Wink-fp 0.0.2

then()

<template>
  <div>
    {{ name }}
  </div>
</template>

<script>
let getAPI = async () => ({
  data: {
    name: 'Sam'
  }
});

let mounted = function() {
  getAPI().then(x => this.name = x.data.name);
};

export default {
  name: 'app',
  data: () => ({
    name: ''
  }),
  mounted
}
</script>

第 8 行

let getAPI = async () => ({
  data: {
    name: 'Sam'
  }
});

getAPI() 模仿由 Axios 回傳的 Promise

第 1 行

<template>
  <div>
    {{ name }}
  </div>
</template>

將 API 結果透過 data binding 顯示在 HTML。

14 行

let mounted = function() {
  getAPI().then(x => this.name = x.data.name);
};

mounted() 內呼叫 getAPI(),由於回傳 Promise,只能透過 then() 讀到 Promise 內部資料,然後指定到 Vue 的 name data 做 data binding。

這種寫法乍看可行,但 this.name 其實是 side effect,在 then() 內我們希望是 pure function 實踐 pipeline,所以嚴格來說不是最好寫法。

await

let mounted = async function() {
  this.name = await getAPI().then(x => x.data.name);
};

then() 內只負責從 Promise 取得 object 的 data.name,是 pure function 也是 pipeline,但 then() 回傳仍是 Promise

最後再透過 awaitPromise 取出資料處理 side effect 部分。

這種寫法 then() 內維持 pure function,而 side effect 則最後由 await 處理,符合 FP 原則,是比較理想寫法。

pipe()

import { pipe, path, then } from 'ramda';

let mounted = pipe(
  getAPI,
  then(path(['data', 'name'])),
  then(console.log)
);

接下來要談更 FP 寫法,在 Vue 2 還無法使用,但將來 Vue 3 的 function API 則可完全發揮。

其實由以上寫法可發現, getAPI() 回傳 Promise,再將 Promise 傳給 then() 的 callback,最後再以 side effect 寫到 data 做 data binding,本質上就是 pipeline。

因此可使用 pipe()getAPI()then() 串起來,之間以 Promise 傳遞,其中 pipe()then() 為 Ramda 的 function。

但最後問題出在 side effect 部分,由於 Vue 2 需依賴 this 存取 data,因此無法簡單使用,所以在此僅以 console.log 代替,但證明 Promise 與 pipeline 概念可行,在 Vue 3 將可使用此寫法。

compose()

import { compose, path, then } from 'ramda';

let mounted = compose(
  then(console.log),
  then(path(['data', 'name'])),
  getAPI
);

也可使用另外一種更 FP 思考方式,既然能將 getAPI()then() 使用 Promise 以 pipeline 方式傳遞,那我們乾脆將 getAPI()then() 透過 compose() 組合成 mounted()

Conclusion

  • 由於 Vue 2 大量依賴 this,使 function composition 在 Vue 2 龍困淺灘, pipe()compose() 只有在 Vuex 時才能發揮,但在 Vue 3 的 function API 時將會徹底改觀
  • 在 Vue 2 雖然不方便使用 function composition 處理 Promise,但最少將 then() 保持 pure function,最後利用 await 處理 side effect,如此可將 side effect 降到最低,實務上很容易維護