# Promise
```typescript
const promise = () => {
let time = Math.round(300 + 500 * Math.random())
return new Promise((resolve, reject) => {
console.log(`run ${time}ms`)
if (Math.random() > 0.5) {
setTimeout(resolve, time, `promise resolve in ${time}ms`)
} else {
setTimeout(reject, time, `promise reject in ${time}ms`)
}
})
}
```
```typescript
// 成功機率是 0.5 * 0.5 * 0.5 = 0.125
const PromiseAll = async () => {
try {
const result = await Promise.all([promise(), promise(), promise()]) // 每個 promise 都會執行,但 promise 的結果必須要全部成功,否則 reject ,可以確保所有的 promise 都是執行成功的
console.log(result)
} catch (e) {
console.log(e)
}
}
```
```typescript
// 成功機率 0.5%
const PromiseRace = async () => {
try {
const result = await Promise.race([promise(), promise(), promise()]) // 每個 promise 都會執行,race 只關心第一個 resolve 或是 reject 的 promise ,
// 應用情景會是當我其中一個 promise 是 error 就 cancel 所有的 fetch request ,可以查看 racePromiseWithCancelFetchRequest 這個 functions
console.log(result)
} catch (e) {
console.log(e)
}
}
```
```typescript
// 成功機率是 100%
const PromiseAllSettled = async () => {
try {
const result = await Promise.allSettled([promise(), promise(), promise()]) // 每個 promise 都會執行,allSettled 不會 throw error 而是會顯示 promise 的 status 以及 reason 如下
// [{ status: 'rejected', reason: 'promise reject in 590ms' },
// { status: 'rejected', reason: 'promise reject in 416ms' },
// { status: 'fulfilled', value: 'promise resolve in 539ms' }]
console.log(result)
} catch (e) {
console.log(e)
}
}
```
```typescript
// 成功機率是 1 - 0.5 * 0.5 * 0.5 = 0.875
const PromiseAny = async () => {
try {
const result = await Promise.any([promise(), promise(), promise()]) // 每個 promise 都會執行,any 用於觀察哪個 promise 執行最快,並所有的 promise 都 rejected 才會進行錯誤處理
//並且 error 的 instance 是 AggregateError 代表 All promises were rejected
console.log(result)
} catch (e) {
if (e instanceof AggregateError) {
console.log('all promise rejected')
}
}
}
```
```typescript
const LoadPlugin = async () => {
// 假設你有兩個來源可以 import ,就可以透過 promise.any 決定哪個 module 優先執行。
const lodash = await Promise.any([
import('https://primary.example.com/lodash'),
import('https://secondary.example.com/lodash'),
]);
}
```
```typescript
// 只要其中一個 fetch request 有 error 立即停止正在進行的 request
const racePromiseWithCancelFetchRequest = async () => {
const controller = new AbortController();
try {
await Promise.race([
fetch('/api/1', { signal: controller.signal }),
fetch('/api/2', { signal: controller.signal }),
fetch('/api/3', { signal: controller.signal }),
new Promise((_, reject) => setTimeout(reject, 1000))
])
} catch (e) {
controller.abort()
}
}
```
## 總結
不管是 `promise.any` 或是 `promise.all` 等等所有的 `promise` 都會去執行,差別只是在於 `return` 的結果,與總體的執行時間。
## AggregateError
`AggregateError` 常用於表示一個多個 `error` 的物件,用於 `promise.any`
```typescript
try {
throw new AggregateError([new Error("some error")], "Hello");
} catch (e) {
console.log(e instanceof AggregateError); // true
console.log(e.message); // "Hello"
console.log(e.name); // "AggregateError"
console.log(e.errors); // [ Error: "some error" ]
}
```
## promise like
判斷一個值是否是 `promiseLike` ,`promise` 是 `es6` 添加的語法,在 `promise` 出現以前先有` promise a+` 規則去定義什麼是 `promise` ,所以我們其實自己寫一個構造函數只要符合` promise A+` 規範,也可以當作 `promise` 來使用,所以才會延伸出 `promiseLike` 的 `interface` 。
@link https://promisesaplus.com/
1. “promise” is an object or function with a then method whose behavior conforms to this specification.
1. “thenable” is an object or function that defines a then method.
1. “value” is any legal JavaScript value(including undefined, a thenable, or a promise).
1. “exception” is a value that is thrown using the throw statement.
1. “reason” is a value that indicates why a promise was rejected.
```typescript
function isPromiseLike(value: any) {
// 1
if (value !== null && (typeof value === 'object' || typeof value === 'function')) {
try {
// 2
if (typeof value.then === 'function') {
const result = value.then()
// 3
if (
(
typeof result === 'object'
|| typeof result === 'function'
|| typeof result === 'string'
|| typeof result === 'number'
|| typeof result === 'boolean'
)
|| typeof result === 'undefined')
return true
}
}
}
catch (e) {
// 4 5
return true
}
}
return false
}
const actualPromise = new Promise((resolve, reject) => {
setTimeout(() => resolve('Actual Promise'), 500)
})
const mockThenable = {
then(resolve: any, reject: any) {},
}
const mockThenable2 = {
then(resolve: any, reject: any) {
return undefined
},
}
const mockThenable3 = {
then(resolve: any, reject: any) {
return null
},
}
const mockThenable4 = {
then(resolve: any, reject: any) {
throw new Error('Actual Promise')
},
}
const mockThenable5 = {
then(resolve: any, reject: any) {
return new Date()
},
}
const nonPromiseObject = { key: 'value' }
const nonPromiseFunction = function () { }
console.log(isPromiseLike(actualPromise))// true
console.log(isPromiseLike(mockThenable))// true
console.log(isPromiseLike(mockThenable2))// true
console.log(isPromiseLike(mockThenable3))// false
console.log(isPromiseLike(mockThenable4))// true
console.log(isPromiseLike(mockThenable5))// true
console.log(isPromiseLike(nonPromiseObject)) // false
console.log(isPromiseLike(nonPromiseFunction)) // false
```