在 MDN 中對(duì) Promise 的定義是:Promise 對(duì)象用于表示一個(gè)異步操作的最終狀態(tài)(完成或失斄菩濉)线召,以及其返回的值。
Promise 對(duì)象存在以下三種狀態(tài):
- pending: 初始狀態(tài)多矮;
- fulfilled:成功狀態(tài)缓淹;
- rejected:失敗狀態(tài);
Promise 對(duì)象的初始狀態(tài)是 pending塔逃,最終狀態(tài)是 fulfilled 或者 rejected讯壶,其中 fulfilled 表示成功狀態(tài),rejected 表示失敗狀態(tài)湾盗。狀態(tài)只能夠由 pending 變成 fulfilled 或 rejected鹏溯,當(dāng) Promise 對(duì)象的狀態(tài)改變后就不可以再改變了。
Promise 簡(jiǎn)單的使用方式如下
let pro = new Promise((resolve, reject) => {
// 執(zhí)行異步操作代碼
// 異步操作成功淹仑,則 resolve(value)
// 異步操作失敗丙挽,則 reject(error)
});
從上面的使用方式來(lái)看,我們可以知道 Promise 是一個(gè)構(gòu)造函數(shù)匀借,其參數(shù)接受一個(gè)匿名函數(shù)颜阐,該匿名函數(shù)有兩個(gè)參數(shù),分別是 resolve 和 reject 函數(shù)吓肋。
通過(guò) new Promise 得到一個(gè) Promise 對(duì)象凳怨,Promise 構(gòu)造函數(shù)所接受的匿名函數(shù)會(huì)直接執(zhí)行,一般是在里面執(zhí)行異步操作是鬼。
當(dāng)異步操作成功的時(shí)候肤舞,執(zhí)行 resolve 函數(shù),會(huì)把 Promise 對(duì)象的狀態(tài)由 pending 變?yōu)?fulfilled均蜜;當(dāng)異步操作失敗的時(shí)候李剖,執(zhí)行 reject 函數(shù),會(huì)把 Promise 對(duì)象的狀態(tài)由 pending 變?yōu)?rejected囤耳。其中 resolve 和 reject 函數(shù)都分別傳入了返回值(value)和錯(cuò)誤值(error)篙顺,這兩個(gè)值將會(huì)作為 Promise 對(duì)象 then 方法的兩個(gè)回調(diào)函數(shù)的實(shí)參。
Promise 對(duì)象上具有很多方法充择,其方法是掛載在對(duì)應(yīng)的原型即 Promise.prototype 上面的德玫,下面就 Promise 對(duì)象的方法做一個(gè)詳細(xì)的介紹。
Promise.prototype.then(onFulfilled, onRejected)
當(dāng) Promise 對(duì)象的狀態(tài)由 pending 變?yōu)?fulfilled 或 rejected 的時(shí)候椎麦,會(huì)觸發(fā) Promise 對(duì)象的 then 方法宰僧。
pro.then((value) => {
// fulfilled
}, (error) => {
// rejected
});
then 方法接受兩個(gè)回調(diào)函數(shù)作為參數(shù),當(dāng) Promise 對(duì)象當(dāng)狀態(tài)由 pending 變?yōu)?fulfilled 的時(shí)候观挎,執(zhí)行第一個(gè)回調(diào)函數(shù)琴儿;當(dāng) Promise 對(duì)象當(dāng)狀態(tài)由 pending 變?yōu)?rejected 的時(shí)候查刻,執(zhí)行第二個(gè)回調(diào)函數(shù)(可選)。這兩個(gè)回調(diào)函數(shù)的參數(shù)分別是執(zhí)行 resolve 和 reject 函數(shù)時(shí)傳入的值凤类。
舉個(gè)簡(jiǎn)單的例子
let timeoutPro = new Promise((resolve, reject) => {
setTimeout(resolve, 1000, 'resolve');
});
timeoutPro.then((value) => {
console.log(value);
});
我們還可以將其封裝起來(lái)
function timeout(ms) {
return new Promise((resolve, reject) => {
setTimeout(resolve, ms, 'resolve');
});
}
timeout(1000).then((value) => {
console.log(value);
});
下面是一個(gè)用 Promise對(duì)象實(shí)現(xiàn)的 Ajax 操作的例子
let get = function (url) {
return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.responseType = "json";
xhr.setRequestHeader("Accept", "application/json");
xhr.send();
xhr.onreadystatechange = () => {
if (xhr.readyState != 4) return;
if (xhr.status == 200) {
resolve(xhr.response);
} else {
reject(xhr.statusText);
}
};
});
};
get("0010.json").then((res) => {
console.log(res.name); // ttsy
}, (error) => {
console.log(error);
});
注:這里 0010.json 為一個(gè)與存放上述代碼的腳本文件同目錄的一個(gè) json 文件,里面的內(nèi)容如下
{
"name":"ttsy",
"age":25
}
then 方法返回的是一個(gè)新的 Promise 對(duì)象普气。因此可以采用鏈?zhǔn)綄?xiě)法谜疤,即 then 方法后面再調(diào)用另一個(gè) then 方法。而下一個(gè) then 方法的行為跟前一個(gè) then 方法的返回值有關(guān)现诀。
- 如果上一個(gè) then 中的回調(diào)函數(shù)返回一個(gè)值夷磕,那么該 then 返回的 Promise 對(duì)象將會(huì)成為 fulfilled 狀態(tài),并且上一個(gè) then 的回調(diào)函數(shù)返回的值將作為下一個(gè) then 的回調(diào)函數(shù)的參數(shù)值仔沿;
- 如果上一個(gè) then 中的回調(diào)函數(shù)拋出一個(gè)錯(cuò)誤坐桩,那么該 then 返回的 Promise 對(duì)象將會(huì)成為 rejected 狀態(tài),并且上一個(gè) then 的回調(diào)函數(shù)拋出的錯(cuò)誤將作為下一個(gè) then 的回調(diào)函數(shù)的參數(shù)值封锉;
- 如果上一個(gè) then 中的回調(diào)函數(shù)返回一個(gè) Promise 對(duì)象绵跷,那么下一個(gè) then 則會(huì)根據(jù)返回的 Promise 對(duì)象的狀態(tài)變化來(lái)執(zhí)行。
舉個(gè)例子~
當(dāng)上一個(gè) then 中的回調(diào)函數(shù)返回一個(gè)值時(shí)
timeout(1000).then((value) => {
return value;
}).then((value) => {
console.log(value); // resolve
})
當(dāng)上一個(gè) then 中的回調(diào)函數(shù)拋出一個(gè)錯(cuò)誤時(shí)
timeout(1000).then((value) => {
throw 'error'
}).then((value) => {
console.log(value);
}, (error) => {
console.log(error) // error
})
當(dāng)上一個(gè) then 中的回調(diào)函數(shù)返回一個(gè) Promise 對(duì)象時(shí)
timeout(1000).then((value) => {
return timeout(1000);
}).then((value) => {
console.log(value); // resolve
}, (error) => {
console.log(error)
})
一般來(lái)講成福,在 then 中的回調(diào)函數(shù)返回一個(gè) Promise 對(duì)象的場(chǎng)景都是 http 請(qǐng)求的嵌套碾局,上述例子只是為了更方便的表示這種情況,實(shí)際場(chǎng)景中并不會(huì)去這么使用奴艾。
上面說(shuō)的是下一個(gè) then 方法的行為跟前一個(gè) then 方法的返回值有關(guān)净当,但如果一開(kāi)始創(chuàng)建的 Promise 對(duì)象的狀態(tài)由 pending 變?yōu)?rejected 時(shí),又是怎樣的情況呢蕴潦?
function timeoutReject(ms) {
return new Promise((resolve, reject) => {
setTimeout(reject, ms, 'reject');
});
}
timeoutReject(1000).then((value) => {
console.log(value);
}).then(undefined, (error) => {
console.log(error) // error
})
可以看到像啼, Promise 對(duì)象的狀態(tài)變?yōu)槭顟B(tài)時(shí)執(zhí)行第二個(gè) then 中的第二個(gè)回調(diào)函數(shù)的代碼。實(shí)際上潭苞,Promise 對(duì)象的錯(cuò)誤具有「冒泡」性質(zhì)忽冻,會(huì)一直向后傳遞,直到被捕獲為止此疹。
Promise.prototype.catch(onRejected)
Promise.prototype.catch(onRejected) 與 Promise.prototype.then(undefined, onRejected) 是一樣的效果甚颂,用于指定發(fā)生錯(cuò)誤時(shí)的回調(diào)函數(shù)。同樣的秀菱,catch 也返回一個(gè)新的 Promise 對(duì)象振诬。
timeoutReject(1000).then((value) => {
console.log(value);
}).catch((error) => {
console.log(error) // error
})
而在上面說(shuō)到,Promise 對(duì)象的錯(cuò)誤具有「冒泡」性質(zhì)衍菱,會(huì)一直向后傳遞赶么,直到被捕獲為止。所以當(dāng)前面的 Promise 對(duì)象拋出錯(cuò)誤時(shí)脊串,則會(huì)依次冒泡辫呻,直到被 catch 捕獲清钥。
timeoutReject(1000).then((value) => {
console.log(value);
}).then((value) => {
console.log(value);
}).catch((error) => {
console.log(error) // error
})
上述代碼中,catch 會(huì)捕獲 timeoutReject() 和兩個(gè) then 所返回的 Promise 對(duì)象所拋出的錯(cuò)誤放闺。
一般來(lái)說(shuō)祟昭,不要在 then 方法里面定義 Reject 狀態(tài)的回調(diào)函數(shù)(即 then 的第二個(gè)參數(shù)),總是使用 catch 方法怖侦。
Promise.all()
Promise.all 方法用于將多個(gè) Promise 對(duì)象合成一個(gè) Promise 對(duì)象篡悟,它接受一個(gè)數(shù)組作為參數(shù),數(shù)組中的每一個(gè)元素都是 Promise 對(duì)象匾寝。最終返回一個(gè)新的 Promise 對(duì)象搬葬,其狀態(tài)由傳入的 Promise 對(duì)象共同決定,分為以下兩種情況艳悔。
- 當(dāng)傳入的 Promise 對(duì)象的狀態(tài)都變成 fulfilled 時(shí)急凰,返回的新的 Promise 對(duì)象的狀態(tài)才會(huì)變成 fulfilled,其 then 方法的回調(diào)函數(shù)的參數(shù)是一個(gè)數(shù)組猜年,分別是傳入的 Promise 對(duì)象的返回值抡锈;
- 當(dāng)傳入的 Promise 對(duì)象的狀態(tài)其中有一個(gè)變成 rejected 時(shí),返回的新的 Promise 對(duì)象的狀態(tài)就會(huì)變成 rejected乔外,其 then 方法的回調(diào)函數(shù)的參數(shù)是第一個(gè)狀態(tài)變成 reject 的 Promise 對(duì)象的返回值企孩。
傳入的 Promise 對(duì)象的狀態(tài)都變成 fulfilled 時(shí)
function timeout1(ms) {
return new Promise((resolve, reject) => {
setTimeout(resolve, ms, 'resolve');
});
}
function timeout2(ms) {
return new Promise((resolve, reject) => {
setTimeout(resolve, ms, 'resolve');
});
}
Promise.all([timeout1(1000), timeout2(2000)]).then((value) => {
console.log(value); // [ 'resolve', 'resolve' ]
}).catch((error) => {
console.log(error)
})
當(dāng)傳入的 Promise 對(duì)象的狀態(tài)其中有一個(gè)變成 rejected 時(shí)
function timeout(ms) {
return new Promise((resolve, reject) => {
setTimeout(resolve, ms, 'resolve');
});
}
function timeoutReject(ms) {
return new Promise((resolve, reject) => {
setTimeout(reject, ms, 'reject');
});
}
Promise.all([timeout(1000), timeoutReject(2000)]).then((value) => {
console.log(value);
}).catch((error) => {
console.log(error) // reject
})