ES6中的Promise詳解

Promise 在 JavaScript 中很早就有各類的開源實現,ES6 將其歸入了官方標準,提供了原生 api 支持,使用更加便捷。javascript

定義

Promise 是一個對象,它用來標識 JavaScript 中異步操做的狀態(pending, resolve, reject)及結果(data)。java

從控制檯打印出來一個Promise 對象來看下
ajax


能夠看到,它是一個構造函數,既有屬於本身私有的 resolve, reject, all, race等方法,也有protype 原型上的 then, catch等方法。

基本用法

  • 模擬問題:3秒後請求完成,返回數據res.data
var p = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('res.data');
  }, 3000);
});

p.then((val) => {
  console.log(val);
  // 'res.data'
});

console.log(p);
// [object Promise]

Promise.resolve() 的用法

Promise.resolve()方法能夠將現有對象轉爲Promise 對象。api

var p = Promise.resolve($.ajax('/something.data'));
p.then((val) => {console.log(val)});

它等價於
var p = new Promise(resolve => {
    resolve($.ajax('/something.data'))
});
p.then((val) => {console.log(val)});

Promise.reject() 用法

此方法和Promise.resolve()方法相似,除了rejecet 表明狀態爲 Rejected,很少說。數組

Promise.all() 用法

用於將多個Promise 實例包裝成一個新的 Promise實例,參數爲一組 Promise 實例組成的數組。promise

var p  = Promise.all([p1,p2,p3]);

當 p1, p2, p3 狀態都 Resolved 的時候,p 的狀態纔會 Resolved;只要有一個實例 Rejected ,此時第一個被 Rejected 的實例的返回值就會傳遞給 P 的回調函數。app

應用場景:假設有一個接口,須要其它兩個接口的數據做爲參數,此時就要等那兩個接口完成後才能執行請求。異步

var p1 = new Promise((resolve, reject) => {
    setTimeout(resolve, 1000'P1');
});
var p2 = new Promise((resolve, reject) => {
    setTimeout(resolve, 2000'P2');
});
// 同時執行p1和p2,並在它們都完成後執行then:
Promise.all([p1, p2]).then((results) => {
    console.log(results); // 得到一個Array: ['P1', 'P2']
});

Promise.race() 用法

和Promise.all 相似,區別是 Promise.race() 只要監聽到其中某一個實例改變狀態,它的狀態就跟着改變,並將那個改變狀態實例的返回值傳遞給回調函數。async

應用場景: 能夠經過多個異步任務來進行容錯處理,多個接口返回一樣的數據,只要有一個接口生效返回數據便可。函數

Promise.prototype.then()

then 方法是定義在 Promise 的原型對象上的,做用是爲 Promise 實例添加狀態改變時的回調函數;

then() 返回一個新的Promise 實例,所以能夠支持鏈式寫法。

鏈式寫法的一個例子//來自廖雪峯

// 0.5秒後返回input*input的計算結果:
function multiply(input) {
    return new Promise((resolve, reject) => {
        console.log('calculating ' + input + ' x ' + input + '...');
        setTimeout(resolve, 500input * input);
    });
}

// 0.5秒後返回input+input的計算結果:
function add(input) {
    return new Promise((resolve, reject) => {
        console.log('calculating ' + input + ' + ' + input + '...');
        setTimeout(resolve, 500input + input);
    });
}

var p = new Promise((resolve, reject) => {
    console.log('start new Promise...');
    resolve(123);
});

p.then(multiply)
 .then(add)
 .then(multiply)
 .then(add)
 .then(function (result) {
    console.log('Got value: ' + result);
});

Promise.prototype.catch()

catch 方法是一個語法糖,看下面代碼就明白了,用於指定發生錯誤時的回調函數。

var p = new Promise((resolve, rejecet) => {
    if (...) {resolve()};
    else {reject()};
})
p.then((val) => {
    console.log('resolve:', val);
}).catch((err) => console.log('reject:', err));

// 等同於

p.then((data) => {
    console.log(data);
}, (err) => {
    console.log(err);
})

// 後一種寫法更好,語義更清晰,第一種方法在第一個函數裏面出錯的話時在第二個函數裏監聽不到變化的。

Promise.try()

實際開發中,常常遇到一種狀況:不知道或者不想區分,函數 f 是同步函數仍是異步操做,可是想用 Promise 來處理它。由於這樣就能夠無論f是否包含異步操做,都用 then 方法指定下一步流程,用 catch 方法處理 f 拋出的錯誤。

  1. 第一種方法,缺陷是不能識別同步請求。
const f = () => console.log('now');
Promise.resolve().then(f);
console.log('next');
// next
// now
  1. new Promise() 寫法
const f = () => console.log('now');
(
() => new Promise(
resolve => resolve(f())
)
)();
console.log('next');
// now
// next
  1. Promise.try 寫法,替代new Promise() 方法,更簡潔。
const f = () => console.log('now');
Promise.try(f);
console.log('next');
// now
// next

總結

兩個特色

  1. 狀態不受外界影響,只有異步操做的結果會影響到它,pending(進行中),reject(已失敗),resolved(已完成)
  2. 狀態只能改變一次

感覺:

  1. promise 首先是一個構造函數,因此須要new 出來一個實例來使用
  2. 像是一個 ajax 函數外面加了一層包裹層,封裝了一下下,實現了代碼層面的同步效果
  3. 頗有意思~(廢話)
  4. 缺點也很明顯,就是代碼語義化不夠,一眼看去都是Promise 的 API,then catch 等等,不能很快明白代碼究竟想表達什麼意思,這也是 async 函數出現的緣由,async ,多是異步操做的終極方案了。
相關文章
相關標籤/搜索