---
# System prepended metadata

title: Promise

---

# 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
```