Promise 最先出現是爲了解決編程中的異步行爲致使的回調地獄。在沒有 Promise 以前,對於函數的異步行爲,通常採用回調函數的方式,在一個函數調用結束觸發回調函數,這樣會致使多層級的回調函數,難以維護。 Promise 有兩個參數,一個成功回調,一個失敗回調,因此在 Promise 外部,能夠準確的得到成功和失敗的時機,而且 Promise 支持鏈式調用,這樣能夠方便的進行屢次調用,可是永遠都是單層級,便於維護。javascript
回調地獄方式html
var sayhello = function (name, callback) {
setTimeout(function () {
console.log(name);
callback();
}, 1000);
}
sayhello("first", function () {
sayhello("second", function () {
sayhello("third", function () {
console.log("end");
});
});
});
//輸出: first second third end
複製代碼
Promise 寫法java
let sayHello = new Promise((resolve, reject) => {
setTimeout(() => {
console.log("first");
resolve();
}, 1000);
});
sayHello
.then(() => {
console.log("second");
})
.then(() => {
console.log("third");
})
.then(() => {
console.log("end");
});
複製代碼
咱們能夠發現 Promise 無論有多少次邏輯處理,每一次只有一層,清晰可見,不像回調同樣,層層嵌套,難以理清。編程
Promise 實例數組
let promise = new Promise(
function(resolve, reject) { // executor(執行者)
setTimeout(()=>{
resolve("done"),1000);
});
複製代碼
咱們只須要 new Promise便可建立一個 Promise,建立即馬上調用其中的執行者。 executor 接受兩個參數:resolve 和 reject 。這些是JavaScript 引擎預約義的,不要咱們建立,咱們只須要在咱們想要告知的狀態中調用對應的方法便可。promise
let promise = new Promise(function(resolve, reject) {
resolve("done");
reject(new Error("…")); // 被忽略
setTimeout(() => resolve("…")); // 被忽略
});
複製代碼
這兒只能有一個結果或一個 error executor 只能調用一個 resolve
或一個 reject
。任何狀態的更改都是最終的。 全部其餘的再對 resolve
和 reject
的調用都會被忽略 而且,resolve/reject
只須要一個參數(或不包含任何參數),而且將忽略額外的參數markdown
那麼咱們會疑問,咱們費這麼大工夫,在 Promise 內部作這麼多操做,最後使他產生一個狀態是爲了什麼,他失敗與否和咱們以前的回調地獄有什麼關係?異步
state 和 result 都是內部的
Promise 對象的 state 和 result 屬性都是內部的。
咱們沒法直接訪問它們。但咱們能夠對它們使用 .then/.catch/.finally 方法。
咱們在下面對這些方法進行了描述。
複製代碼
上面咱們使用 Promise 生產了一個成功或者失敗的結果,能夠經過使用 .then
、.catch
和 .finally
方法爲消費函數進行結果接收。函數
let promise = new Promise(function(resolve, reject) {
setTimeout(() => resolve("done!"), 1000);
});
// resolve 運行 .then 中的第一個函數
promise.then(
result => alert(result), // 1 秒後顯示 "done!"
error => alert(error) // 不運行
);
複製代碼
.then
的第一個參數是一個函數,該函數將在 promise resolved 後運行並接收結果。 .then
的第二個參數也是一個函數,該函數將在 promise rejected 後運行並接收 error。 若是咱們只對成功完成的狀況感興趣,那麼咱們能夠只爲 .then
提供一個函數參數:oop
let promise = new Promise(resolve => {
setTimeout(() => resolve("done!"));
}, 1000);
promise.then(alert); // 1 秒後顯示 "done!"
複製代碼
.catch(f)
調用是 .then(null, f)
的徹底的模擬,它只是一個簡寫形式。簡單說,catch就是一個接收錯誤結果的方法。 咱們能夠對 settled 的 promise 附加處理程序 若是 promise 爲 pending 狀態,.then/catch/finally
處理程序(handler)將等待它。不然,若是 promise 已是 settled 狀態,它們就會運行 自測案例 寫一個 3s 後彈窗的 Promise
function delay(ms) {
// 你的代碼
return new Promise(resolve,reject){
setTimeout(resolve,3000)
}
}
delay(3000).then(() => alert('runs after 3 seconds'));
複製代碼
若是隻要失敗回調,那麼只須要將 then 的第一個參數設置爲null, 也可使用 catch ,這裏面能夠接受 reject 的結果
let promise = new Promise((resolve, reject) => {
setTimeout(() => reject(new Error("Whoops!")), 1000);
});
// .catch(f) 與 promise.then(null, f) 同樣
promise.catch(alert); // 1 秒後顯示 "Error: Whoops!"
複製代碼
不管結果如何,最後都會執行這裏面的函數。 **finally()**
方法返回一個Promise
。在promise結束時,不管結果是fulfilled或者是rejected,都會執行指定的回調函數。這爲在Promise
是否成功完成後都須要執行的代碼提供了一種方式。 這避免了一樣的語句須要在then()
和catch()
中各寫一次的狀況。
在我瞭解完 Promise 後,我對如何實現他很是感興趣,因而我試着本身來構建一個 Promise。 首先咱們要分析一下咱們的需求,咱們要獲得什麼,要實現哪些功能,肯定目標。
咱們按照上圖,分爲兩個大步驟,開始進行實現咱們本身的 Promise。
首先構造 Promise 類。
class Promise {
constructor(executor) {
if (typeof executor !== "function") {
console.log("參數不合法");
} else {
this.status = "pending";
this.value = undefined;
this.error = undefined;
//自動運行Promise中的函數
try {
//將resolve和reject函數給使用者
executor(resolve, reject);
} catch (e) {
//若是在函數中拋出異常則將它注入reject中
reject(e);
}
}
}
resolve(data) {
//成功觸發
if (this.status === "pending") {
this.status = "fulfilled";
this.value = data;
}
}
reject(data) {
//失敗觸發
if (this.status === "pending") {
this.status = "rejected";
this.error = data;
}
}
then() {}
catch() {}
finally() {}
}
複製代碼
咱們實現了上述幾個目標,接下來咱們要實現接受結果信息的方法。
class Promise {
constructor(executor) {
if (typeof executor !== "function") {
console.log("參數不合法");
} else {
this.status = "pending";
this.value = undefined;
this.error = undefined;
//自動運行Promise中的函數
try {
//將resolve和reject函數給使用者
executor(this.resolve, this.reject);
} catch (e) {
//若是在函數中拋出異常則將它注入reject中
this.reject(e);
}
}
}
resolve = (data) => {
//成功觸發
if (this.status === "pending") {
this.status = "fulfilled";
this.value = data;
}
};
reject = (data) => {
//失敗觸發
if (this.status === "pending") {
this.status = "rejected";
this.error = data;
}
};
then(onFulfilled, onRejected) {
if (this.status === "fulfilled") {
onFulfilled(this.value);
}
if (this.status === "rejected") {
onRejected(this.error);
}
}
catch(onRejected) {
if (this.status === "rejected") {
onRejected(this.error);
}
}
finally(onFinally) {
if (this.status !== "pending") {
onFinally();
}
}
}
複製代碼
這樣,咱們就完成了一個簡易版的 Promise。 咱們來將文件引入測試一下,看看結果如何。
<!DOCTYPE html>
<html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> </head> <body> <script src="./index.js"></script> <script> let promise = new Promise((resolve, reject) => { resolve("coolFish!"); }); promise.then( (data) => { console.log("成功" + data); }, (data) => { console.log("失敗" + data); } ); </script> </body> </html>
複製代碼
結果和 Promise 同樣,能夠實現成功和失敗的不一樣操做,接下來咱們要開始擴展它的功能,從可支持鏈式調用開始。
首先咱們明確 promise.then(onFulfilled, onRejected ) 作的事情
then(onFulfilled, onRejected) {
//建立並返回一個Promise實例
return new Promise((resolve, reject) => {
let wrapOnFulfilled = () => {
setTimeout(() => {
try {
console.log("wrapOnFulfilled");
let x = onFulfilled(this.value);
resolve(x);
} catch (error) {
reject(error);
}
}, 0);
};
let wrapOnRejected = () => {
setTimeout(() => {
try {
console.log("wrapOnRejected");
let x = onRejected(this.error);
resolve(x);
} catch (error) {
reject(error);
}
}, 0);
};
if (this.status === "fulfilled") {
wrapOnFulfilled();
} else if (this.status === "rejected") {
wrapOnRejected();
} else {
this.onFulfilledCallbacks.push(wrapOnFulfilled);
this.onRejectedCallbacks.push(wrapOnRejected);
}
});
}
複製代碼
首先咱們先明確目標 Promise.all
接受一個 promise 數組做爲參數(從技術上講,它能夠是任何可迭代的,但一般是一個數組)並返回一個新的 promise。 當全部給定的 promise 都被 settled 時,新的 promise 纔會 resolve,而且其結果數組將成爲新的 promise 的結果。 咱們來看一張流程圖,而後咱們按照流程圖來實現咱們的代碼
all(promises) {
return new Promise((resolve, reject) => {
// 若是Promise.all接收到的是一個空數組([]),它會當即決議。
if (!promises.length) {
resolve([]);
}
let result = [];
let resolvedPro = 0;
for (let index = 0, length = promises.length; index < length; index++) {
Promise.resolve(promises[index]).then(
(data) => {
// 注意,這裏要用index賦值,而不是push。由於要保持返回值和接收到的promise的位置一致性。
result[index] = data;
if (++resolvedPro === length) {
resolve(result);
}
},
(error) => {
reject(error);
}
);
}
});
}
複製代碼
// 須要注意的是,若是Promise.race接收到的是一個空數組([]),則會一直掛起,而不是當即決議。
Promise.race = function(promises) {
return new Promise((resolve, reject) => {
promises.forEach((promise) => {
Promise.resolve(promise).then(resolve, reject);
});
});
};
複製代碼
// Promise.allSettled 返回一個在全部給定的promise都已經fulfilled或rejected後的promise,
// 並帶有一個對象數組,每一個對象表示對應的promise結果。
Promise.allSettled = function(promises) {
return new Promise((resolve, reject) => {
if (!promises.length) {
resolve([]);
}
let result = [];
let resolvedPro = 0;
for (let index = 0, length = promises.length; index < length; index++) {
Promise.resolve(promises[index])
.then((data) => {
// 注意,這裏要用index賦值,而不是push。由於要保持返回值和接收到的promise的位置一致性。
result[index] = {
status: FULFILLED_STATE,
value: data,
};
if (++resolvedPro === length) {
resolve(result);
}
})
.catch((error) => {
result[index] = {
status: REJECTED_STATE,
reason: error,
};
if (++resolvedPro === length) {
resolve(result);
}
});
}
});
};
複製代碼