Promise Constructor 最常用來配合 setTimeout()
,在 Callback 中以 Closure 取得 resolve()
與 reject()
建立 Promise,事實上當以 sleep()
取代 setTimeout()
後,就可改以 Function Pipeline 取代 Promise Constructor。
Version
macOS Catalina 10.15.6
ECMAScript 2015
Ramda 0.27.0
Wink-fp 0.1.2
Promise constructor
let f = ms => x => new Promise((resolve, reject) => {
let inc = () => {
let result = x + 1
return (result % 2) ?
resolve(`fulfilled: ${result}`) :
reject(`rejected: ${result}`)
}
setTimeout(inc, ms)
})
f(3000)(1).then(console.log).catch(console.log)
f(3000)(2).then(console.log).catch(console.log)
第 2 行
let inc = () => {
let result = x + 1
return (result % 2) ?
resolve(`fulfilled: ${result}`) :
reject(`rejected: ${result}`)
}
inc()
是 synchronous function,若 + 1
結果是 奇數
,則回傳 Fulfilled Promise;若結果為 偶數
,則回傳 Rejected Promise。
第 10 行
setTimeout(inc, ms)
重點是 inc()
並不是立即執行,而是 setTimeout()
後 3 秒才執行,所以回傳是 Promise。
若要在 asynchronous function 產生 Promise,只靠 Promise.resolve()
與 Promise.reject()
寫不出來,必須使用 Promise Constructor,透過其 executor function 傳進 resolve()
與 reject()
,則可在 setTimeout()
的 callback 內由 resolve()
與 reject()
建立 Promise 回傳。
sleep()
let sleep = ms => new Promise(resolve => setTimeout(resolve), ms)
let inc = x => {
let result = x + 1
return (result % 2) ?
Promise.resolve(`fulfilled: ${result}`) :
Promise.reject(`rejected: ${result}`)
}
let f = x => sleep(3000).then(() => inc(x))
f(1).then(console.log).catch(console.log)
f(2).then(console.log).catch(console.log)
其實明眼人應該會發現,executor function 的 resolve()
與 reject()
幾乎是為了 setTimeout()
量身定做,讓 setTimeout()
的 callback 可以 closure 取得 resolve()
與 reject()
建立 Promise。
第 1 行
let sleep = ms => new Promise(resolve => setTimeout(resolve), ms)
ES6 則可使用 promise-based 的 sleep()
取代 ES5 callback-based 的 setTimeout()
。
第 3 行
let inc = x => {
let result = x + 1
return (result % 2) ?
Promise.resolve(`fulfilled: ${result}`) :
Promise.reject(`rejected: ${result}`)
}
如此則可使用 Promise.resolve()
與 Promise.reject()
,不再一定得用 Promise Constructor。
11 行
let f = x => sleep(3000).then(() => inc(x))
因為 sleep()
回傳 promise,因此可改用 then()
執行 inc()
。
Wink-fp
import { sleep, resolve, reject, log } from 'wink-fp'
let inc = x => {
let result = x + 1
return (result % 2) ?
resolve(`fulfilled: ${result}`) :
reject(`rejected: ${result}`)
}
let f = ms => x => sleep(ms).then(() => inc(x))
f(3000)(1).then(log).catch(log)
f(3000)(2).then(log).catch(log)
Wink-fp 已經提供 sleep()
、resolve()
、reject()
與 log()
,可直接使用。
Function Pipeline
import { pipe, andThen as then, otherwise, add, ifElse, useWith, flip, thunkify } from 'ramda'
import { sleep, resolve, reject, log, formatN, isOdd } from 'wink-fp'
let resolveLog = pipe(
formatN(1)(`fulfilled: {0}`),
resolve
)
let rejectLog = pipe(
formatN(1)(`rejected: {0}`),
reject
)
let inc = pipe(
add(1),
ifElse(isOdd, resolveLog, rejectLog)
)
let f = useWith(
flip(then), [sleep, thunkify(inc)]
)
pipe(
f(3000),
then(log),
otherwise(log)
)(1)
pipe(
f(3000),
then(log),
otherwise(log)
)(2)
其實 inc()
與 f()
都有 pipeline 特質,可使用 pipe()
與 then()
加以 Function Pipeline。
Conclusion
- 有兩個場景特別適合使用 Promise Constructor:
- 需同時產生 Fulfilled Promise 與 Rejected Promise 時
- 須在 asynchronous function 同時產生 Fulfilled promise 與 Rejected promise 時
- Promise Constructor 與
setTimeout()
幾乎無法使用 Function Pipeline,若改用 Promised-based 的sleep()
,則可搭配 Ramda 與 Wink-fp 使用 Function Pipeline
Reference
Marius Schulz, Create a New Promise in JavaScript with Promise Constructor
MDN, Promise