promise 使用竟態機制以及在實際中的應用

竟態,意思是程序的運行結果與時間有關,可能前一秒是 A 結果但是推遲幾秒後是 B 結果。

什麼時候用到竟態

當我們需要判斷異步方法執行順序或時間的時候:

function delay(time = 1000) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      reject("超時");
    }, time);
  });
}

Promise.race([test(), delay(3000)]).then(
  () => {
    // test()及時完成
  },
  error => {
    // foo產生錯誤或者沒有在3秒之內完成
  }
);
// 同理於
// Promise.race([test(), delay(3000)]).then(
//   () => {
//     // test()及時完成
//   }).catch(
//   error => {
//     // foo產生錯誤或者沒有在3秒之內完成
//   }
// );

先分析一下上面代碼:我們創建了一個 delay 函數,這個函數返回一個 promisepromise 設置定時 time 時間執行 reject。promise 如果不給他返回 resolve 或者 reject 狀態那麼他將一直處於 pending 狀態。promise 的 race 方法接收一個數組作爲參數,返回最先執行完的那個參數方法,如果該方法爲 resolve 就返回 resolve 如果是 reject 就返回 reject
在上述例子中,race 的參數數組有 testdelay,如果 test 在 3 秒之內完成了並且 test 方法裏沒有 reject 操作,那麼就會走到 then 第一個方法;如果 test 執行出了錯誤(使用 reject 拋出錯誤)或者沒有在 3 秒之內完成,那麼 3 秒後就會執行 delay 函數,走 then 第二個方法,也就是 catch 操作,而 delay 函數此時就會 reject 返回’超時’值。

實際應用
在實際應用中我們可以預先判斷一個接口的超時操作,超過多長時間就認定爲超時:

function delay(time = 1000) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      reject("請求超時");
    }, time);
  });
}

let p = Promise.race([fetch("/api/.."), delay(3000)]);
p.then(res => {
  console.log(res);
});
p.catch(error => {
  console.error(error);
});

另外,我們也可以藉助 async 在其他場景使用:

function delay(time = 1000) {
  return new Promise((resolve, reject) => {
    setTimeout(resolve, time);
  });
}

async function foo() {
  // fetch data 結束後等待一段時間再進行某項操作

  await delay(2000);

  // do something...
}

比如在生產中我們需要在請求完接口成功後,提示成功彈層 1s 再跳轉界面。我們不知道請求需要花費多長時間,但是我們能知道請求何時完成,當請求完成的時候開始計時,到了時間後再去進行我們需要的操作。

delay 方法返回一個 promise,裏面使用 setTimeout 定時改變 promise 狀態爲 resolve,所以就能做到 2000 毫秒後得到 resolve 狀態,再執行後續代碼。上面我們講過如果 promise 不返回任何狀態的話它會處於 pending 狀態阻塞後續代碼執行。

我們來看一個有趣的例子:

async function foo() {
  await new Promise(() => {});
  console.log("1");
}
foo();

async/await 和 promise 一樣,async 會返回一個 promise,await 會等待 promise 執行。當 Promise 沒有狀態返回的時候,console.log("1");不執行。當我們返回 resolve/reject:

async function foo() {
  await new Promise(resolve => resolve(2));
  console.log("1");
}

foo();

這時會打印出“1”,既然能夠控制它返回成功或者失敗狀態,就可以在返回狀態時進行時間控制。

爲什麼不直接用 setTimeout,因爲 setTimeout 無法判斷異步結果是成功還是失敗。

發佈了108 篇原創文章 · 獲贊 25 · 訪問量 18萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章