> For the complete documentation index, see [llms.txt](https://mistborn.gitbook.io/til-coding/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://mistborn.gitbook.io/til-coding/javascript/promise.md).

# Promise

想像有一位超級歌星，粉絲日以繼夜關心專輯什麼時候出，這位超級歌星很煩惱，因此想出了一個辦法，讓粉絲追蹤他的 IG ，若有任何關於他的消息都可以第一時間知道。\
在編寫程式碼也會遇到一樣的問題：\
1\. 生產程式碼：作一些事需要時間，像是加載腳本，腳色是歌手。\
2\. 消費程式碼：知道生產程式碼的結果，不論有沒有成功，腳色是粉絲。\
3\.  promise 是連接 2 者的橋樑，生產程式碼的結果會保證傳到每個消費程式碼，腳色是 IG。

```javascript
// syntax，executor 是生產程式碼，最終會產生結果，傳到 promise，promise 物件有 2 個內部屬性，
// state 描述 promise 狀態，一開始 pedding => fullfilled or rejected
// result 任意值，一開始 undefined => value or error
let promise = new Promise(function(resolve, reject) {
  // executor (生产者代码，"singer")
});
```

![](/files/-Li15LZedPu5z52TthkC)

```javascript
// 會自動呼叫 executor，executor 會接收 resolve、reject 2 個參數。
let promise = new Promise(function(resolve, reject) {
  // the function is executed automatically when the promise is constructed

  // after 1 second signal that the job is done with the result "done"
  setTimeout(() => resolve("done"), 1000);
});
```

![](/files/-Li17_l3ViTCZTCZ4iZF)

```javascript
let promise = new Promise(function(resolve, reject) {
  // after 1 second signal that the job is finished with an error
  setTimeout(() => reject(new Error("Whoops!")), 1000);
});
```

![](/files/-Li17gdkko5Hopi8boLD)

```javascript
// executor 只會有一個結果，另一個結果會被忽略。
let promise = new Promise(function(resolve, reject) {
  resolve("done");

  reject(new Error("…")); // 被忽略
  setTimeout(() => resolve("…")); // 被忽略
});

// 可以使用任何類型的參數帶入 reject，但建議使用 Error 物件

// 通常 executor 會執行一些異步程式，這時需要一些時間才能得到結果。我們可以立即返回結果，當任務
// 已經完成。
let promise = new Promise(function(resolve, reject) {
  // not taking our time to do the job
  resolve(123); // immediately give the result: 123
});

// promise 的內建屬性 state and result，不能從消費程式碼取得，但可以透過
// .then/.catch/.finally 等方法取得
```

## Consumers: then, catch, finally

&#x20;消費程式碼可以得知生產結果，透過 `.then`, `.catch` and `.finally`等方法。

### then

```javascript
// syntax，成功時執行第 1 個參數，失敗時執行第 2 個參數
promise.then(
  function(result) { /* handle a successful result */ },
  function(error) { /* handle an error */ }
);

// 成功
let promise = new Promise(function(resolve, reject) {
  setTimeout(() => resolve("done!"), 1000);
});

// resolve runs the first function in .then
promise.then(
  result => alert(result), // shows "done!" after 1 second
  error => alert(error) // doesn't run
);

// 失敗
let promise = new Promise(function(resolve, reject) {
  setTimeout(() => reject(new Error("Whoops!")), 1000);
});

// reject runs the second function in .then
promise.then(
  result => alert(result), // doesn't run
  error => alert(error) // shows "Error: Whoops!" after 1 second
);

// 只返回成功結果
let promise = new Promise(resolve => {
  setTimeout(() => resolve("done!"), 1000);
});

promise.then(alert); // shows "done!" after 1 second
```

### catch

```javascript
// 只返回失敗結果，可以用 .then(null, errorHandlingFunction) 或 .catch(errorHandlingFunction)
// .catch(f) 是 .then(null, f) 的簡寫
let promise = new Promise((resolve, reject) => {
  setTimeout(() => reject(new Error("Whoops!")), 1000);
});

// .catch(f) is the same as promise.then(null, f)
promise.catch(alert); // shows "Error: Whoops!" after 1 second
```

### finally

```javascript
// 就像 try {...} catch {...} 有 finally，promise 也有 finally
// .finally(f) 很像 .then(f, f)，當 promise 完成他會被執行無論成功或失敗。
// finally 適合使用在清空記憶體
new Promise((resolve, reject) => {
  /* do something that takes time, and then call resolve/reject */
})
  // runs when the promise is settled, doesn't matter successfully or not
  .finally(() => stop loading indicator)
  .then(result => show result, err => show error)
  
// .finally(f) 跟 .then(f, f) 的差別
// 1. finally 沒有參數
// 2. finally 傳遞 result 參數
// 成功
new Promise((resolve, reject) => {
  setTimeout(() => resolve("result"), 2000)
})
  .finally(() => alert("Promise ready"))
  .then(result => alert(result)); // <-- .then handles the result

// 失敗
new Promise((resolve, reject) => {
  throw new Error("error");
})
  .finally(() => alert("Promise ready"))
  .catch(err => alert(err));  // <-- .catch handles the error object
 
// 3. .finally(f) 比 .then(f, f) 簡潔

// 當 promise 執行完畢結果會立即回傳
// an immediately resolved promise
let promise = new Promise(resolve => resolve("done!"));

promise.then(alert); // done! (shows up right now)
```

## Example: loadScript

```javascript
function loadScript(src, callback) {
  let script = document.createElement('script');
  script.src = src;

  script.onload = () => callback(null, script);
  script.onerror = () => callback(new Error(`Script load error for ${src}`));

  document.head.append(script);
}

// 重新改寫
function loadScript(src) {
  return new Promise(function(resolve, reject) {
    let script = document.createElement('script');
    script.src = src;

    script.onload = () => resolve(script);
    script.onerror = () => reject(new Error(`Script load error for ${src}`));

    document.head.append(script);
  });
}

// 使用
let promise = loadScript("https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js");

promise.then(
  script => alert(`${script.src} is loaded!`),
  error => alert(`Error: ${error.message}`)
);

promise.then(script => alert('One more handler to do something else!'));
```

| Promises                                                                                                                                                                                                                                                    | Callbacks                                                                                                                                                                                |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Promises allow us to do things in the natural order. First, we run `loadScript(script)`, and `.then` we write what to do with the result.                                                                                                                   | We must have a `callback` function at our disposal when calling `loadScript(script, callback)`. In other words, we must know what to do with the result *before* `loadScript` is called. |
| We can call `.then` on a Promise as many times as we want. Each time, we’re adding a new “fan”, a new subscribing function, to the “subscription list”. More about this in the next chapter: [Promises chaining](https://javascript.info/promise-chaining). | There can be only one callback.                                                                                                                                                          |
