選單
GSS 技術部落格
在這個園地裡我們將從技術、專案管理、客戶對談面和大家分享我們多年的經驗,希望大家不管是喜歡或是有意見,都可以回饋給我們,讓我們有機會和大家對話並一起成長!
若有任何問題請來信:gss_crm@gss.com.tw
3 分鐘閱讀時間 (677 個字)

然後咧? ES6 Promise 的奇技淫巧

es6promise

隨著 async/await 功能普及,現在寫 JavaScript 時,比較沒機會用上 Promise.then() 。但最近寫 Cypress 測試時發現, Cypress command 也用上了類似 Promise 的介面。

他們採用了 Bluebird 這個 Promise 實作,又在外面包了一個稱為 Chainer 的結構。那個 Chainer 有著自己的 then() 。

這時候就無法使用專門為 Promise 設計的 async/await 。 

...

Introduction to Cypress - Commands Are Promises

Cypress 文件中, Commands Are Promises 段落的最後,其實提到了為何 command 不是 Promise 。

 於是我想分享一些以前使用 ES6 Promise 的小技巧。

 chaining

Promise 最有名的就是 chaining 了, chaining 讓你可以避開 callback hell !

以在 Node.js 中讀檔為例:

fs.access(filepath1, fs.constants.F_OK, (err) => {
  if (err) return;
  fs.readFile(filepath1, (err, file) => {
    if (err) return;
    console.log(file);
  });
}); 

如果我們用上 Promise 介面的 fs-extra ,上面的程式可以改寫成:

const fs = require('fs-extra');

fs.pathExists(filepath)
  .then(exists => fsex.readFile(filepath))
  .then(file => console.log(file)); 

有趣的是,按 Promise/A+ 規格 2.2.7 小節, then 的參數(也就是 onFulfilled 和 onRejected 兩個函數)最後 return 的值,不管是不是 Promise ,都要再經過一次 Promise 解析程序( Promise Resolution Procedure )。

2.2.7.1 If either onFulfilled or onRejected returns a value x, run the Promise Resolution Procedure [[Resolve]](promise2, x).

Promises/A+

於是我們可以自由傳回同步或非同步的結果,在下一個 onFulfilled 函數中,都能直接取用:

const example = fetch('http://example.com').then(res => res.text());
const ans = Promise.resolve(42);

ans
  .then(x => {
    console.log(x); // 42
    return Promise.resolve(x);
  })
  .then(x => {
    console.log(x); // 還是 42 ,沒有被 Promise 包起來
    return example;
  })
  .then(x => {
    console.log(x); // example.com 的內容
  }); 

也可以利用這點,往後傳遞額外資訊,但要混用同步與非同步資料,就會稍微麻煩一些:

getUser()
  .then(user =>
    getPosts(user.id).then(posts => ({ user, posts }))
  )
  .then(({ user, posts }) => {
    console.log(user, posts);
  }); 

 recursion

Promise 只會 resolve 一次,而且內容確定下來後不會改變,所以我們也可以在遞迴中使用它們:

function add(pa, pb) {
  return pa.then(a => pb.then(b => a + b));
}

function fib(i) {
  if (i === 0 || i === 1) return Promise.resolve(1);
  return add(fib(i - 1), fib(i - 2));
}

fib(5).then(x => console.log(x)); // 8 

但因為 Promise/A+ 2.2.4 節的設計限制,讓 onFulfilled 和 onRejected 不會馬上執行,所以這樣的技巧無法用在迴圈中。

2.2.4 onFulfilled or onRejected must not be called until the execution context stack contains only platform code.

Promises/A+

 小結

Promise.then() 這種介面越來越常見,例如 Java 的 CompletableFuture 有著 thenApply() 、 C# 的 Task 有 ContinueWith() 。在這些環境中,也能用上同樣的技巧。

希望這些經驗可以幫助大家在沒有 async/await 的時候,簡化您的程式。

如何選擇測試工具來進行自動化測試
每日小知識 #12 - Container 資安(3)

相關文章

 

評論

尚無評論
已經注冊了? 這裡登入
Guest
2024/05/14, 週二

Captcha 圖像