Promise 對象的理解

Promise 含義

Promise 是異步編程的一種解決方案,比傳統的解決方案——回調函數和事件——更合理和更強大。它由社區最先提出和實現,ES6 將其寫進了語言標準,統一了用法,原生提供了 Promise 對象。javascript

所謂 Promise,簡單說就是一個容器,裏面保存着某個將來纔會結束的事件(一般是一個異步操做)的結果。從語法上說,Promise 是一個對象,從它能夠獲取異步操做的消息。Promise 提供統一的 API,各類異步操做均可以用一樣的方法進行處理。html

Promise 對象有如下兩個特色:java

  • 對象的狀態不受外界影響。有三種狀態,分別爲 pending(進行中)、fulfilled(已成功)和 rejected(已失敗)。
  • 一旦狀態改變,就不會再變,任什麼時候候均可以獲得這個結果。狀態改變只有兩種可能:從 pending 變爲 fulfilled 和從 pending 變爲 rejected。

使用 Promise 對象的好處在於:編程

  • 能夠將異步操做以同步操做的流程表達出來,避免了層層嵌套的回調函數。
  • Promise 對象提供統一的接口,使得控制異步操做更加容易。

Promise 缺點:數組

  • 沒法取消 Promise,一旦新建它就會當即執行,沒法中途取消。
  • 若是不設置回調函數,Promise 內部拋出的錯誤,不會反應到外部。
  • 當處於 pending 狀態時,沒法得知目前進展到哪個階段(剛剛開始仍是即將完成)。

基本用法

ES6 規定,Promise 對象是一個構造函數,用來生成 Promise 實例。promise

const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    const num = Math.random();

    if (num > 0.5) {
      resolve(num);
    } else {
      reject(num);
    }
  }, 500);
});

promise.then(
  res => {
    console.log("成功:" + res);
  },
  err => {
    console.log("失敗:" + err);
  }
);

Promise 構造函數接受一個函數做爲參數,該函數的兩個參數分別是 resolve 和 reject。它們是兩個函數,由 JavaScript 引擎提供,不用本身部署。dom

  • resolve 函數的做用:將 Promise 對象的狀態從「未完成(pending)」變爲「成功(resolved)」,在異步操做成功時調用,並將異步操做的結果做爲參數傳遞出去。
  • reject 函數的做用:將 Promise 對象的狀態從「未完成(pending)」變爲「失敗(rejected)」在異步操做失敗時調用,並將異步操做報出的錯誤,做爲參數傳遞出去。
  • then 方法做用:接受兩個回調函數做爲參數。第一個回調函數是 Promise 對象的狀態變爲 resolved 時調用,第二個回調函數是 Promise 對象的狀態變爲 rejected 時調用。第二個函數可選,不必定要提供,也能夠將第二個函數做爲 catch 方法的參數。
  • catch 方法做用:用於指定發生錯誤時的回調函數。Promise 對象異步操做拋出錯誤,狀態就會變爲 rejected,就會調用 catch 方法指定的回調函數處理這個錯誤。另外,then 方法指定的回調函數,若是運行中拋出錯誤,也會被 catch 方法捕獲。
const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    const num = Math.random();

    if (num > 0.5) {
      resolve(num);
    } else {
      reject(num);
    }
  }, 500);
});

promise
  .then(res => {
    console.log("成功:" + res);
  })
  .catch(err => {
    console.log("失敗:" + err);
  });

promise
  .then(res => {
    console.log("成功:" + res);
    throw new Error("test");
  })
  .catch(err => {
    // num > 0.5時打印 "失敗:Error: test"
    console.log("失敗:" + err);
  });

Promise 執行順序

Promise 新建後當即執行,then 方法指定的回調函數,將在當前腳本全部同步任務執行完纔會執行,catch 同理。異步

調用 resolve 或 reject 並不會終結 Promise 的參數函數的執行。異步編程

const promise = new Promise((resolve, reject) => {
  console.log("我是第一個執行的");
  resolve();
});

promise.then(res => {
  console.log("我是第三個執行的");
});

console.log("我是第二個執行的");

resolve 函數和 reject 函數的參數

reject 函數的參數一般是 Error 對象的實例,表示拋出的錯誤;resolve 函數的參數除了正常的值之外,還多是另外一個 Promise 實例。函數

若是一個 Promise(P2) 的 resolve 參數是另外一個 Promise(P1),此時 P1 的狀態就會傳給 P2,P1 的狀態決定了 P2 的狀態,P1 的狀態改變,P2 的回調函數纔會執行。

const p1 = new Promise(function(resolve, reject) {
  setTimeout(() => reject(new Error("fail")), 3000);
});

const p2 = new Promise(function(resolve, reject) {
  setTimeout(() => resolve(p1), 1000);
});

p2.then(result => console.log(result)).catch(error => console.log(error));
// Error: fail

上面代碼中,p1 是一個 Promise,3 秒以後變爲 rejected。p2 的狀態在 1 秒以後改變,resolve 方法返回的是 p1。因爲 p2 返回的是另外一個 Promise,致使 p2 本身的狀態無效了,由 p1 的狀態決定 p2 的狀態。因此,後面的 then 語句都變成針對後者(p1)。又過了 2 秒,p1 變爲 rejected,致使觸發 catch 方法指定的回調函數。

Promise 鏈式調用

then 方法能夠返回一個新的 Promise 實例(注意,不是原來那個 Promise 實例)。所以能夠採用鏈式寫法,即 then 方法後面再調用另外一個 then 方法。

const promise = new Promise((resolve, reject) => {
  resolve("promise");
})
  .then(res => {
    console.log(res); // promise
    return "promise1";
  })
  .then(res => {
    console.log(res); // promise1
    return "promise2";
  })
  .then(res => {
    console.log(res); // promise2
  });

注意:只要一個 Promise 中拋出錯誤,將執行 catch 方法,then 鏈終止。

const promise = new Promise((resolve, reject) => {
  resolve("promise");
})
  .then(res => {
    console.log(res); // promise
    throw new Error("停止");
    return "promise1";
  })
  .then(res => {
    console.log(res);
    return "promise2";
  })
  .then(res => {
    console.log(res);
  })
  .catch(err => {
    console.log(err); // Error: 停止
  });

主動終止 then 鏈,經過 catch 方法來停止 promise chain

const promise = new Promise((resolve, reject) => {
  resolve("promise");
})
  .then(res => {
    console.log(res); // promise
    return Promise.reject({
      notRealPromiseException: true
    });
  })
  .then(res => {
    console.log(res);
    return "promise2";
  })
  .then(res => {
    console.log(res);
  })
  .catch(err => {
    if (err.notRealPromiseException) {
      return true;
    }
    console.log(err);
  });

Promise.prototype.finally()

finally 方法用於指定無論 Promise 對象最後狀態如何,都會執行的操做。該方法是 ES2018 引入標準的。

finally 本質上是 then 方法的特例,不接受任何參數,不依賴於 Promise 的執行結果

promise.finally(() => {
  // 語句
});

// 等同於
promise.then(
  result => {
    // 語句
    return result;
  },
  error => {
    // 語句
    throw error;
  }
);

Promise.all()

Promise.all 方法用於將多個 Promise 實例,包裝成一個新的 Promise 實例。

const promise = Promise.all([promise1, promise2, promise3])

Promise.all 方法接受一個數組做爲參數,promise、pro 米色、promise3 都是 Promise 實例,若是不是,就會先調用下面講到的 Promise.resolve 方法,將參數轉爲 Promise 實例,再進一步處理。(Promise.all 方法的參數能夠不是數組,但必須具備 Iterator 接口,且返回的每一個成員都是 Promise 實例。瞭解 Iterator 接口

promise 的狀態由 promise一、promise二、promise3 決定,分紅兩種狀況。

  • 只有 promise一、promise二、promise3 的狀態都變成 fulfilled,p 的狀態纔會變成 fulfilled,此時 promise一、promise二、promise3 的返回值組成一個數組,傳遞給 p 的回調函數。
  • 只要 promise一、promise二、promise3 之中有一個被 rejected,promise 的狀態就變成 rejected,此時第一個被 reject 的實例的返回值,會傳遞給 promise 的回調函數。
const p1 = new Promise((resolve, reject) => {
  resolve("hello");
})
  .then(result => result)
  .catch(e => e);

const p2 = new Promise((resolve, reject) => {
  throw new Error("報錯了");
})
  .then(result => result)
  .catch(e => e);

Promise.all([p1, p2])
  .then(result => console.log(result))
  .catch(e => console.log(e));
// ["hello", Error: 報錯了]

上面代碼中,p1 會 resolved,p2 首先會 rejected,可是 p2 有本身的 catch 方法,該方法返回的是一個新的 Promise 實例,p2 指向的其實是這個實例。該實例執行完 catch 方法後,也會變成 resolved,致使 Promise.all()方法參數裏面的兩個實例都會 resolved,所以會調用 then 方法指定的回調函數,而不會調用 catch 方法指定的回調函數。

若是 p2 沒有本身的 catch 方法,就會調用 Promise.all()的 catch 方法。

Promise.race()

Promise.race 方法一樣是將多個 Promise 實例,包裝成一個新的 Promise 實例。

const p = Promise.race([p1, p2, p3])

只要 p一、p二、p3 之中有一個實例率先改變狀態,p 的狀態就跟着改變。那個率先改變的 Promise 實例的返回值,就傳遞給 p 的回調函數。

Promise.race 方法的參數與 Promise.all 方法同樣,若是不是 Promise 實例,就會先調用下面講到的 Promise.resolve 方法,將參數轉爲 Promise 實例,再進一步處理。

Promise.resolve()

將現有對象轉化爲 Promise 對象。

const promise = Promise.resolve('Hello world')
  • 參數是 Promise 實例,該方法不作任何改變。
  • 參數是一個 thenable 對象,先將對象轉爲 Promise 對象,而後當即執行 thenable 方法。至關於將 thenable 對象中的 then 方法處理的值做爲參數傳給 promise then 方法。
let thenObj = {
  then(resolve, reject) {
    resolve("Hello");
  }
};

const promise = Promise.resolve(thenObj);
promise.then(res => {
  console.log(res); // Hello
});
  • 參數不是具備 then 方法的對象,或根本就不是對象,則 Promise.resolve 方法返回一個新的 Promise 對象,狀態爲 resolved。

Promise.reject()

Promise.reject(reason)方法也會返回一個新的 Promise 實例,該實例的狀態爲 rejected。

相關文章
相關標籤/搜索