Promise的三種狀態

1. 狀態定義

Promise對象代表一個異步操作的最終完成或失敗,它有且只有三種互斥的狀態:

  1. Pending(進行中)
  • 初始狀態,既不是成功也不是失敗
  • 操作正在進行中,尚未完成
  1. Fulfilled(已成功)
  • 異步操作成功完成
  • 也稱為"resolved"(已解決)
  • 狀態一旦確定,不可再改變
  1. Rejected(已失敗)
  • 異步操作失敗
  • 狀態一旦確定,不可再改變

2. 狀態轉換機制

初始狀態:Pending
       ↓
   ┌──成功執行─────→ Fulfilled
   │    (調用resolve)
Pending
   │    (調用reject或拋出異常)
   └──失敗執行─────→ Rejected

重要特性:
1. 單向流動:只能從Pending → Fulfilled 或 Pending → Rejected
2. 不可逆:一旦轉為Fulfilled或Rejected,狀態凝固不再改變
3. 外部不可變:狀態只能由Promise內部改變,外部只能讀取

3. 狀態特點

  • 確定即凝固:狀態變化是一次性的
  • 不可取消:一旦開始執行,無法取消(但可以通過其他方式模擬)
  • 異步傳播:狀態變化會異步通知所有監聽者(then/catch)

then方法的鏈式調用原理

1. 鏈式調用的核心機制

每個then()方法都返回一個全新的Promise對象,這是鏈式調用的基礎。

原始Promise → then() → 新Promise1 → then() → 新Promise2 → ...

2. 執行流程規則

promise
  .then(onFulfilled1, onRejected1)  // 返回新Promise1
  .then(onFulfilled2, onRejected2)  // 返回新Promise2
  .then(onFulfilled3, onRejected3)  // 返回新Promise3
規則1:回調函數執行時機
  • 當前一個Promise狀態確定後,才執行對應的回調
  • 回調函數總是異步執行(微任務)
規則2:新Promise的狀態決定

新Promise的狀態由回調函數的返回值執行過程決定:

回調函數情況

新Promise狀態

值/原因

返回普通值

Fulfilled

該值

返回Promise

跟隨該Promise

跟隨該Promise

拋出異常

Rejected

拋出的異常

無返回值

Fulfilled

undefined

規則3:狀態傳遞機制

如果then沒有提供對應的回調函數,狀態會穿透到鏈中的下一個Promise:

// 狀態穿透示例
Promise.reject('error')
  .then(null, null)        // 無處理函數,穿透
  .then(onFulfilled, null) // 無reject處理,穿透
  .then(null, onRejected)  // 這裏捕獲錯誤

3. 具體運行原理

3.1 成功鏈(Fulfilled鏈)
Promise.resolve(1)
  .then(x => x + 1)        // 返回2 → 新Promise狀態Fulfilled
  .then(x => x * 2)        // 返回4 → 新Promise狀態Fulfilled
  .then(console.log)       // 輸出4
3.2 錯誤捕獲與恢復
Promise.reject('error1')
  .catch(err => {
    console.log('捕獲:', err);  // 輸出:捕獲: error1
    return '恢復值';             // 返回普通值 → 鏈恢復為Fulfilled
  })
  .then(val => {
    console.log('恢復後:', val); // 輸出:恢復後: 恢復值
  })
3.3 Promise返回的特殊處理
Promise.resolve(1)
  .then(x => {
    return new Promise(resolve => {
      setTimeout(() => resolve(x + 1), 1000);
    }); // 返回Promise,新Promise等待這個Promise解決
  })
  .then(x => {
    console.log(x); // 2(1秒後執行)
  });

4. 內部實現簡化原理

// then方法的簡化實現(原理示意)
class MyPromise {
  constructor(executor) { /* ... */ }
  
  then(onFulfilled, onRejected) {
    // 1. 創建並返回新的Promise
    return new MyPromise((resolve, reject) => {
      // 2. 封裝回調函數
      const handleCallback = () => {
        try {
          // 3. 獲取當前Promise的結果
          const result = this.state === 'fulfilled'
            ? (onFulfilled ? onFulfilled(this.value) : this.value)
            : (onRejected ? onRejected(this.reason) : this.reason);
          
          // 4. 根據回調返回值決定新Promise狀態
          if (result instanceof MyPromise) {
            // 如果返回Promise,等待它的結果
            result.then(resolve, reject);
          } else {
            // 返回普通值,新Promise成功
            resolve(result);
          }
        } catch (error) {
          // 5. 回調拋出異常,新Promise失敗
          reject(error);
        }
      };
      
      // 6. 異步執行(微任務)
      if (this.state === 'pending') {
        this.callbacks.push(handleCallback);
      } else {
        queueMicrotask(handleCallback);
      }
    });
  }
}

5. 鏈式調用的關鍵特性

5.1 異步執行保證
  • then/catch/finally回調總是異步執行
  • 即使Promise已是確定狀態,回調也放入微任務隊列
// 執行順序示例
console.log('1');

Promise.resolve()
  .then(() => console.log('3'))
  .then(() => console.log('4'));

console.log('2');

// 輸出順序:1 → 2 → 3 → 4
5.2 錯誤傳播機制
Promise鏈中的錯誤會一直向後傳遞,直到被捕獲
     ↓ 拋出錯誤
Promise1 → Promise2 → Promise3 → catch
    狀態:Rejected   穿透    穿透    捕獲
5.3 返回值處理
// 返回值影響鏈的繼續
Promise.resolve()
  .then(() => {
    // 情況1:返回普通值,繼續成功鏈
    return 42;
  })
  .then(value => {
    console.log(value); // 42
    
    // 情況2:返回Promise,等待它
    return new Promise(resolve => {
      setTimeout(() => resolve('done'), 1000);
    });
  })
  .then(value => {
    console.log(value); // 'done'(1秒後)
    
    // 情況3:拋出異常,轉為失敗鏈
    throw new Error('失敗');
  })
  .catch(error => {
    console.error(error); // 捕獲錯誤
    // 情況4:catch中返回值,恢復成功鏈
    return '恢復';
  })
  .then(value => {
    console.log(value); // '恢復'
  });

6. 實際應用場景

場景1:順序異步操作
// 用户登錄流程
login(userInfo)
  .then(token => getUserData(token))      // 獲取用户數據
  .then(userData => getPermissions(userData)) // 獲取權限
  .then(perms => initApp(perms))          // 初始化應用
  .catch(error => showError(error));      // 統一錯誤處理
場景2:並行與順序結合
// 先並行請求,再順序處理
Promise.all([fetchData1(), fetchData2()])
  .then(([data1, data2]) => {
    // 並行完成後順序處理
    return processData1(data1)
      .then(result1 => processData2(result1, data2));
  })
  .then(finalResult => {
    // 最終處理
  });
場景3:超時控制
function withTimeout(promise, timeout) {
  return Promise.race([
    promise,
    new Promise((_, reject) => {
      setTimeout(() => reject(new Error('超時')), timeout);
    })
  ]);
}

withTimeout(fetchData(), 5000)
  .then(data => console.log(data))
  .catch(error => console.error('失敗:', error));

7. 常見陷阱與注意事項

陷阱1:忘記返回
// ❌ 錯誤:忘記return,後續then接收undefined
someAsync()
  .then(result => {
    process(result); // 忘記return
  })
  .then(nextResult => {
    // nextResult 是 undefined
  });

// ✅ 正確:總是返回需要的值
someAsync()
  .then(result => {
    return process(result); // 顯式return
  });
陷阱2:錯誤處理不當
// ❌ 錯誤:catch位置不當,無法捕獲前面的錯誤
someAsync()
  .catch(error => handleError(error))  // 只捕獲someAsync的錯誤
  .then(nextAsync)                     // nextAsync的錯誤會逃逸
  .then(anotherAsync);                 // 這裏的錯誤無捕獲

// ✅ 正確:在鏈末端統一捕獲
someAsync()
  .then(nextAsync)
  .then(anotherAsync)
  .catch(error => handleAllErrors(error)); // 捕獲所有錯誤
陷阱3:微任務阻塞
// 避免在微任務中執行耗時操作
Promise.resolve()
  .then(() => {
    // ❌ 避免:長時間同步操作阻塞微任務隊列
    heavyComputation(); // 阻塞後續所有微任務
    
    // ✅ 改為:將耗時任務放入宏任務
    return new Promise(resolve => {
      setTimeout(() => {
        heavyComputation();
        resolve();
      }, 0);
    });
  });

8. 與async/await的關係

async函數本質
// async函數自動返回Promise
async function foo() {
  return 42;
  // 等價於:return Promise.resolve(42);
}

// await本質是then的語法糖
async function bar() {
  const result = await somePromise();
  // 等價於:somePromise().then(result => { ... })
  
  const next = await anotherPromise();
  return next;
}
鏈式調用轉async/await
// Promise鏈
fetchData()
  .then(process)
  .then(save)
  .then(log)
  .catch(handleError);

// 等價async/await
async function handle() {
  try {
    const data = await fetchData();
    const processed = await process(data);
    const saved = await save(processed);
    await log(saved);
  } catch (error) {
    handleError(error);
  }
}

總結要點

Promise狀態核心

  1. 三種狀態:Pending、Fulfilled、Rejected
  2. 單向轉換:一旦確定,不可逆轉
  3. 異步通知:狀態變化通過微任務異步通知

鏈式調用本質

  1. 新Promise生成:每個then都創建新Promise,實現鏈式
  2. 狀態傳遞規則
  • 有回調:按回調返回值決定新狀態
  • 無回調:狀態值穿透到下一環
  1. 錯誤傳播:錯誤沿鏈向後傳遞,直到被捕獲

Promise的鏈式調用是異步編程的基礎,理解其原理有助於編寫更清晰、健壯的異步代碼。在現代JavaScript中,雖然async/await提供了更直觀的語法,但其底層仍然是基於Promise的鏈式調用機制。