第 1 章 Promise的理解和使用
1.1 Promise 是什么?
1.1.1 理解
抽象表達(dá)
Promise
是一門新的技術(shù)(ES6
規(guī)范)拥诡;
Promise
是JS
中進(jìn)行異步編程的新解決方案,備注:舊方案是單純使用回調(diào)函數(shù).
異步編程: fs文件操作、數(shù)據(jù)庫操作鸠补、ajax刻像、定時(shí)器铡恕。
具體表達(dá)
從語法上來說:
Promise
是一個(gè)構(gòu)造函數(shù)铁材;
從功能上來說:Promise
對象用來封裝一個(gè)異步操作并可以獲取其成功/失敗的結(jié)果值问慎。
1.1.2 promise 的狀態(tài)改變
pending
變?yōu)?resolved
;pending
變?yōu)?rejected
;Promise
初始狀態(tài)是pending
型雳。
說明: 只有這 2 種, 且一個(gè)
promise
對象只能改變一次,無論變?yōu)槌晒€是失敗, 都會有一個(gè)結(jié)果數(shù)據(jù)成功的結(jié)果數(shù)據(jù)一般稱為value
, 失敗的結(jié)果數(shù)據(jù)一般稱為reason
当凡。
PromiseState
: Promise
的狀態(tài) 是實(shí)例對象中的一個(gè)屬性 可能的值有:
pending
(未決定的)山害;resolved
(fullfilled
成功的);reject
(rejected
失敗的)沿量;
狀態(tài)的改變只會從 pending => fullfilled 或者 pending => rejected
浪慌;
PromiseResult
是實(shí)例對象中的另一個(gè)屬性 保存著對象【成功/失敗】的結(jié)果 保存異步任務(wù)成功或者失敗的結(jié)果
resolve
;reject
朴则;
1.1.3 Promise初體驗(yàn) : 抽獎(jiǎng)案例
- 頁面上有一個(gè)按鈕當(dāng)點(diǎn)擊按鈕之后1秒 权纤,顯示是否中獎(jiǎng),不管中沒中獎(jiǎng)都給出相應(yīng)的提示信息乌妒。
/**
* 生成隨機(jī)數(shù)
* */
function rand(m, n) {
return Math.ceil(Math.random() * (n - m + 1)) + m - 1;
}
/**
* 1. 點(diǎn)擊按鈕汹想,1s后顯示是否 中獎(jiǎng)(30%概率中獎(jiǎng))
* 1.1 如果中獎(jiǎng)彈出恭喜恭喜,獎(jiǎng)品...
* 1.2 如果未中獎(jiǎng)彈出撤蚊,再接再厲
*/
// 1. 傳統(tǒng)回調(diào)函數(shù)的方式實(shí)現(xiàn)
let btn = document.querySelector('#btn');
/*btn.addEventListener('click', () => {
setTimeout(function () {
let n = rand(1, 100);// 獲取 1~100的隨機(jī)數(shù)
if (n <= 30) {
alert('恭喜恭喜古掏,您中獎(jiǎng)了!');
} else {
alert('很遺憾,請?jiān)賮硪淮?');
}
}, 1000);
});*/
// 2. Promise 形式實(shí)現(xiàn)
/**
* resolve 解決 函數(shù)類型的數(shù)據(jù)
*
* reject 拒絕 函數(shù)類型的數(shù)據(jù)
*/
btn.addEventListener('click', () => {
let p = new Promise((resolve, reject) => {
setTimeout(function () {
let n = rand(1, 100);// 獲取 1~100的隨機(jī)數(shù)
if (n <= 30) {
resolve(n);
} else {
reject(n);
}
}, 1000);
});
/**
* 1. 需求侦啸,將中獎(jiǎng)的數(shù)字顯示出來
*/
p.then(value => {
alert('恭喜恭喜槽唾,您中獎(jiǎng)了! 將號碼是 => ' + value);
},
reason => {
alert('很遺憾,請?jiān)賮硪淮?');
})
});
1.1.4 promise 的基本流程
1.2 為什么要用 Promise?
1.2.1 指定回調(diào)函數(shù)的方式更加靈活
舊的: 必須在啟動(dòng)異步任務(wù)前指定匹中。
promise
: 啟動(dòng)異步任務(wù) => 返回promie
對象 => 給promise
對象綁定回調(diào)函數(shù)(甚至可以在異步任務(wù)結(jié)束后指定/多個(gè))夏漱。
1.2.2 支持鏈?zhǔn)秸{(diào)用, 可以解決回調(diào)地獄問題
1. 什么是回調(diào)地獄?
答:回調(diào)函數(shù)嵌套調(diào)用, 外部回調(diào)函數(shù)異步執(zhí)行的結(jié)果是嵌套的回調(diào)執(zhí)行的條件。
2. 回調(diào)地獄的缺點(diǎn)?
不便于閱讀
不便于異常處理
3. 解決方案?
promise 鏈?zhǔn)秸{(diào)用
4. 終極解決方案?
async/await
1.2.3 Promise封裝 Ajax請求顶捷,點(diǎn)擊按鈕發(fā)送請求
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=no"
,maximum-scale=1.0,minimum-scale=1.0>
<title>Promise封裝ajax請求</title>
<link rel="stylesheet" href="../bootstrap/css/bootstrap.min.css">
</head>
<body>
<div class="container">
<div class="page-header">Promise封裝ajax請求</div>
<button class="btn btn-primary" id="btn">發(fā)送ajax請求</button>
</div>
<script src="../js/jquery.min.js"></script>
<script src="../bootstrap/js/bootstrap.min.js"></script>
<script>
let btn = document.querySelector('#btn');
btn.addEventListener('click', function () {
// https://api.apiopen.top/getJoke
let p = new Promise((resolve, reject) => {
// 1. 創(chuàng)建對象
const xhr = new XMLHttpRequest();
// 2. 初始化
xhr.open('GET', 'https://api.apiopen.top/getJoke');
// 3. 發(fā)送
xhr.send();
// 4. 處理響應(yīng)結(jié)果
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
// 判斷響應(yīng)狀態(tài)碼是否在 2xx
if (xhr.status >= 200 && xhr.status) {
resolve(xhr.response);
} else {
reject(xhr.status);
}
}
}
});
p.then(value => {
console.log(value);
},
reason => {
console.warn(reason);
});
});
</script>
</body>
</html>
1.2.4 Promise封裝ajax請求挂绰,傳遞參數(shù)返回一個(gè)Promise對象
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=no"
,maximum-scale=1.0,minimum-scale=1.0>
<title>Promise封裝ajax請求</title>
</head>
<body>
<script>
/**
* 封裝一個(gè)函數(shù) sendAjax方法GET AJAX 請求
* 參數(shù) URL
* 返回結(jié)果 Promise 對象
*/
function sendAjax(url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.responseType = 'json';
xhr.open('GET', url);
xhr.send();
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(xhr.response);
} else {
reject(xhr.status);
}
}
}
});
}
sendAjax('https://api.apiopen.top/getJoke').then(
value => {
console.log(value);
},
reason => {
console.log(reason);
}
);
</script>
</body>
</html>
1.3 如何使用Promise
1.3.1 API
Promise 構(gòu)造函數(shù): Promise (excutor) {};
executor
函數(shù): 執(zhí)行器(resolve, reject) => {}
服赎;resolve
函數(shù): 內(nèi)部定義成功時(shí)我們調(diào)用的函數(shù)value => {}
葵蒂;reject
函數(shù): 內(nèi)部定義失敗時(shí)我們調(diào)用的函數(shù)reason => {}
說明:executor
會在Promise
內(nèi)部立即同步調(diào)用,異步操作在執(zhí)行器中執(zhí)行。
/**
* Promise 構(gòu)造函數(shù)的 參數(shù)就是一個(gè)執(zhí)行器重虑,執(zhí)行器函數(shù)是在Promise中同步調(diào)用的
* @type {Promise<any>}
*/
let p = new Promise((resolve, reject) => {
// 此函數(shù)內(nèi)部的代碼是同步執(zhí)行的
console.log(11); // 第一打印
// 修改Promise對象的狀態(tài)
reject('error');
});
console.log(22); // 第二打印
/**
* 都是函數(shù)類型的參數(shù)
*/
p.then(value => {
},
reason => {
});
/**
* catch只能指定失敗的回調(diào)
*/
p.catch(reason => {
console.log(reason);
});
Promise.prototype.then 方法: (onResolved, onRejected) => {}
onResolved
函數(shù): 成功的回調(diào)函數(shù)(value) => {}
;onRejected
函數(shù): 失敗的回調(diào)函數(shù)(reason) => {}
;
說明: 指定用于得到成功 value 的成功回調(diào)和用于得到失敗 reason 的失敗回調(diào)返回一個(gè)新的 promise 對象
Promise.prototype.catch 方法: (onRejected) => {}
-
onRejected
函數(shù): 失敗的回調(diào)函數(shù)(reason) => {}
践付。
說明: then()的語法糖, 相當(dāng)于: then(undefined, onRejected)
。
Promise.resolve 方法: (value) => {}
value
: 成功的數(shù)據(jù)或promise
對象缺厉。
說明: 返回一個(gè)成功/失敗的 promise 對象永高。
/**
* Promise對象的resolve方法
* 1. 接收一個(gè)參數(shù)返回一個(gè)成功和失敗的Promise對象
* 2. 如果傳遞的參數(shù)為非Promise類型的對象,則返回的結(jié)果為成功的Promise對象
* 3. 如果傳遞的參數(shù)是一個(gè)Promise對象提针,則參數(shù)的結(jié)果決定了 resolve的結(jié)果
*/
let p1 = Promise.resolve('成功');
console.log(p1);
/**
* 傳遞的參數(shù)是一個(gè)Promise對象 返回是成功
* @type {Promise<any>}
*/
let p2 = Promise.resolve(new Promise((resolve, reject) => {
resolve('OK');
}));
console.log(p2);
/**
* 傳遞的參數(shù)是一個(gè)Promise對象 返回的是失敗
* @type {Promise<any>}
*/
let p3 = Promise.resolve(new Promise((resolve, reject) => {
reject('Error');
}));
console.log(p3);
p3.catch(reason => {
console.log(reason); // Error 對處理失敗進(jìn)行處理
});
Promise.reject 方法: (reason) => {}
-
reason
: 失敗的原因命爬。
說明: 返回一個(gè)失敗的 promise 對象。
/**
* Promise中的reject方法
* 1. reason:失敗的原因
* 2. 如果傳遞的是一個(gè)非Promise對象辐脖,其結(jié)果是一個(gè)失敗的Promise
* 3. 如果傳遞的是一個(gè)Promise,不管是返回成功的值還是失敗的值其處理結(jié)果都是失敗的Promise
*/
let p1 = Promise.reject('失敗!');
console.log(p1);
let p2 = Promise.reject(new Promise((resolve, reject) => {
resolve('成功!');
}));
console.log(p2);
let p3 = Promise.reject(new Promise((resolve, reject) => {
reject('Error');
}));
console.log(p3);
Promise.all 方法: (promises) => {}
-
promises
: 包含 n 個(gè)promise
的數(shù)組饲宛。
說明: 返回一個(gè)新的 promise, 只有所有的 promise 都成功才成功, 只要有一個(gè)失敗了就直接失敗。
let p1 = new Promise((resolve, reject) => {
resolve('OK!');
});
let p2 = Promise.resolve('Success!');
let p3 = Promise.resolve('resolve!');
let result = Promise.all([p1, p2, p3]); // 只有三個(gè)同時(shí)返回成功才會成功嗜价,否則將返回失敗
console.log(result);
Promise.race 方法: (promises) => {}
-
promises
: 包含 n 個(gè)promise
的數(shù)組艇抠。
說明: 返回一個(gè)新的 promise, 第一個(gè)完成的 promise 的結(jié)果狀態(tài)就是最終的結(jié)果狀態(tài)幕庐。
/**
* 返回一個(gè)新的Promise,第一個(gè)完成的Promise的結(jié)果狀態(tài)就是最終的狀態(tài)
*/
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('OK!');
}, 1000);
});
let p2 = Promise.resolve('Success!');
let p3 = Promise.resolve('resolve!');
let result = Promise.race([p1, p2, p3]); // 只有三個(gè)同時(shí)返回成功才會成功家淤,否則將返回失敗
console.log(result);
Promise 封裝讀取文件函數(shù)
/**
* 封裝一個(gè)函數(shù) mineReadFile讀取文件內(nèi)容
* 參數(shù): path文件路徑
* 返回: Promise對象
*/
const fs = require('fs');
function mineReadFile(path) {
return new Promise((resolve, reject) => {
fs.readFile(path, (err, data) => {
if (err) reject(err);
resolve(data);
});
});
}
mineReadFile('../resources/context.txt').then(value => {
console.log(value.toString());
},
reason => {
console.log(reason);
});
const fs = require('fs');
/**
* 1. 使用傳統(tǒng)的方式
*/
fs.readFile('../resources/context.txt', (err, data) => {
if (err) throw new Error(err);
console.log(data.toString());
});
// 2. 使用Promise的方式
let p = new Promise((resolve, reject) => {
fs.readFile('../resources/context.txt', (err, data) => {
if (err) reject(err);
resolve(data.toString());
});
});
p.then(value => {
console.log('------------------------------------------------------------------------');
console.log(value.toString());
},
reason => {
console.log(reason);
});
util.promisify簡化Promise
-
util.promisify
是NodeJS
的API
异剥。 參考網(wǎng)址
/**
* 傳入一個(gè)遵循常見的錯(cuò)誤優(yōu)先的回調(diào)風(fēng)格的函數(shù)(即以(err,value) => 回調(diào)作為最后一個(gè)參數(shù)),并返回一個(gè)Promise的版本
*/
// 引入util 模塊
const util = require('util'); // 引入util這個(gè)模塊
// 引入fs模塊
const fs = require('fs');
// 返回一個(gè)新函數(shù)
/**
* util.promisify
*/
let mineReadFile = util.promisify(fs.readFile);
mineReadFile('../resources/context.txt').then(value => {
console.log(value.toString());
});
1.3.2 promise 的幾個(gè)關(guān)鍵問題
如何改變 promise 的狀態(tài)?
開始
Promise
對象的初始化狀態(tài)是pending
絮重。resolve(value)
: 如果當(dāng)前是pending
就會變?yōu)?code>resolved届吁。reject(reason)
: 如果當(dāng)前是pending
就會變?yōu)?rejected
。拋出異常: 如果當(dāng)前是
pending
就會變?yōu)?rejected
绿鸣。
/**
* 代碼中如何修改Promise對象的狀態(tài)
*/
let p = new Promise((resolve,reject) => {
// 1. 調(diào)用resolve函數(shù) 將對象的狀態(tài) 從 pending 修改為 fullfilled
// resolve('OK');
// 2. 調(diào)用reject函數(shù) pending => rejected
// reject('Error');
// 3. 拋出異常
throw new Error('出問題了!');
});
一個(gè) promise 指定多個(gè)成功/失敗回調(diào)函數(shù), 都會調(diào)用嗎?
- 當(dāng)
promise
改變?yōu)閷?yīng)狀態(tài)時(shí)都會調(diào)用。
/**
* 為一個(gè)Promise對象指定多個(gè)回調(diào)暂氯,當(dāng)他們的狀態(tài)改變的時(shí)候都會調(diào)用潮模,如果一直是pending狀態(tài)的話就不會調(diào)用其回調(diào)
*/
let p = new Promise((resolve, reject) => {
resolve('OK');
});
p.then(value => {
console.log(value);
});
p.then(value => {
alert(value);
});
改變 promise 狀態(tài)和指定回調(diào)函數(shù)誰先誰后?
都有可能, 正常情況下是先指定回調(diào)再改變狀態(tài), 但也可以先改狀態(tài)再指定回調(diào)。
如何先改狀態(tài)再指定回調(diào)?
在執(zhí)行器中直接調(diào)用
resolve()/reject()
痴施。
延遲更長時(shí)間才調(diào)用then()
擎厢。
- 什么時(shí)候才能得到數(shù)據(jù)?
如果先指定的回調(diào), 那當(dāng)狀態(tài)發(fā)生改變時(shí), 回調(diào)函數(shù)就會調(diào)用, 得到數(shù)據(jù)。
如果先改變的狀態(tài), 那當(dāng)指定回調(diào)時(shí), 回調(diào)函數(shù)就會調(diào)用, 得到數(shù)據(jù)辣吃。
promise.then()返回的新 promise 的結(jié)果狀態(tài)由什么決定?
簡單表達(dá): 由 then()指定的回調(diào)函數(shù)執(zhí)行的結(jié)果決定动遭。
詳細(xì)表達(dá):
如果拋出異常, 新 promise 變?yōu)?rejected, reason 為拋出的異常。
如果返回的是非 promise 的任意值, 新 promise 變?yōu)?resolved, value 為返回的值神得。
如果返回的是另一個(gè)新 promise, 此 promise 的結(jié)果就會成為新 promise 的結(jié)果厘惦。
promise 如何串連多個(gè)操作任務(wù)
promise 的 then()返回一個(gè)新的 promise, 可以開成 then()的鏈?zhǔn)秸{(diào)用。
通過 then 的鏈?zhǔn)秸{(diào)用串連多個(gè)同步/異步任務(wù)哩簿。
promise 異常傳透?
當(dāng)使用 promise 的 then 鏈?zhǔn)秸{(diào)用時(shí), 可以在最后指定失敗的回調(diào)宵蕉。
前面任何操作出了異常, 都會傳到最后失敗的回調(diào)中處理。
中斷 promise 鏈?
當(dāng)使用 promise 的 then 鏈?zhǔn)秸{(diào)用時(shí), 在中間中斷, 不再調(diào)用后面的回調(diào)函數(shù)节榜。
辦法: 在回調(diào)函數(shù)中返回一個(gè) pendding 狀態(tài)的 promise 對象羡玛。
第 2 章 自定義(手寫) Promise
2.1 函數(shù)版本
/**
* 執(zhí)行多個(gè)回調(diào)的實(shí)現(xiàn)
* @param executor
* @constructor
*/
function Promise(executor) {
// 3. 添加屬性
this.PromiseState = 'pending';
this.PromiseResult = null;
// 聲明一個(gè)屬性
this.callBacks = [];
// 預(yù)先保存this的值
const _this = this;
function resolve(data) {
// 修改對象的狀態(tài) PromiseState 屬于實(shí)例對象的屬性
// 設(shè)置結(jié)果值 PromiseResult 屬于實(shí)例對象的屬性
// 對象狀態(tài)只允許修改一次
if (_this.PromiseState !== 'pending') return;
_this.PromiseState = 'fullfilled';
_this.PromiseResult = data;
// 在異步任務(wù)的時(shí)候,調(diào)用回調(diào)函數(shù)的時(shí)機(jī)
// 循環(huán)遍歷執(zhí)行函數(shù)
setTimeout(() => {
_this.callBacks.forEach(item => {
item.onResolved(data);
});
});
}
function reject(data) {
// 對象狀態(tài)只允許修改一次
if (_this.PromiseState !== 'pending') return;
_this.PromiseState = 'rejected';
_this.PromiseResult = data;
// 在異步任務(wù)的時(shí)候宗苍,調(diào)用回調(diào)函數(shù)的時(shí)機(jī)
/**
* 循環(huán)遍歷執(zhí)行多個(gè)函數(shù)
*/
setTimeout(() => {
_this.callBacks.forEach(item => {
item.onRejected(data);
});
});
}
// 執(zhí)行器函數(shù)式同步調(diào)用的
// 調(diào)用執(zhí)行器函數(shù)
try {
executor(resolve, reject);
} catch (e) {
reject(e);
}
}
// 1. 添加then方法
Promise.prototype.then = function (onResolved, onRejected) {
// 保存this的值
const _this = this;
// 判斷回調(diào)函數(shù)的參數(shù) 是否為一個(gè)函數(shù)類型
if (typeof onRejected !== 'function') {
/**
* 實(shí)現(xiàn)異常穿透的原理
* @param reason
*/
onRejected = reason => {
throw reason;
}
}
/**
* Promise實(shí)現(xiàn)值傳遞
*/
if (typeof onResolved !== 'function') {
onResolved = value => value;
}
return new Promise((resolve, reject) => {
function callBack(type) {
/**
* 當(dāng)出現(xiàn)異常的時(shí)候
*/
try {
// 獲取回調(diào)函數(shù)的執(zhí)行結(jié)果
let result = type(_this.PromiseResult);
if (result instanceof Promise) {
// 如果是Promise類型的對象
result.then(value => {
resolve(value);
},
reason => {
reject(reason);
})
} else {
// 返回非Promise對象的時(shí)候狀態(tài)為成功
resolve(result);
}
} catch (e) {
reject(e);
}
}
// 調(diào)用回調(diào)函數(shù) 成功失敗的狀態(tài)是 由PromiseState決定
if (this.PromiseState === 'fullfilled') {
setTimeout(() => {
callBack(onResolved);
});
}
if (this.PromiseState === 'rejected') {
setTimeout(() => {
callBack(onRejected);
});
}
// 判斷 pending的狀態(tài)
if (this.PromiseState === 'pending') {
// 回調(diào)的執(zhí)行時(shí)機(jī)稼稿?
// 保存回調(diào)函數(shù) 重要 重要 重要
// 8. 實(shí)現(xiàn)執(zhí)行多個(gè)回調(diào)
this.callBacks.push({
onResolved: function () {
callBack(onResolved);
},
onRejected: function () {
callBack(onResolved);
}
});
}
});
};
// 12. 添加catch方法
Promise.prototype.catch = function (onRejected) {
/**
* 直接調(diào)用then方法
*/
return this.then(undefined, onRejected);
};
// 13. 添加Promise.resolve方法
/**
* Promise對象的 resolve 方法
* @param value
* @returns {Promise}
*/
Promise.resolve = function (value) {
return new Promise((resolve, reject) => {
// 判斷value 的類型
if (value instanceof Promise) {
value.then(value => {
resolve(value);
}, reason => {
reject(reason);
});
} else {
resolve(value);
}
});
};
// 14. 添加 reject 方法
/**
* 為Promise對象添加 reject 方法
* @param reason
* @returns {Promise}
*/
Promise.reject = function (reason) {
return new Promise((resolve, reject) => {
reject(reason);
});
};
// 15. 添加all方法
/**
* 為Promise對象添加 all 方法
* @param promises
* @returns {Promise}
*/
Promise.all = function (promises) {
// 定義一個(gè)計(jì)數(shù)變量 計(jì)算當(dāng)前Promise成功的個(gè)數(shù)
let count = 0;
let arr = [];
return new Promise((resolve, reject) => {
// 遍歷 promises
for (let i = 0; i < promises.length; i++) {
promises[i].then(value => {
// 得知對象的狀態(tài)是成功
// 狀態(tài)成功就會走到該函數(shù)中 進(jìn)行count++ 操作
count++;
// 將當(dāng)前promise對象成功的結(jié)果存到一個(gè)數(shù)組中保持原有的順序
arr[i] = value;
// 判斷
if (count === promises.length) {
// 修改此時(shí)Promise的狀態(tài)
resolve(arr);
}
},
reason => {
reject(reason);
});
}
});
};
// 16 . race 方法 接受一個(gè)參數(shù) Promise 對象數(shù)組 其結(jié)果由數(shù)組中最先改變狀態(tài)的那個(gè)值決定
/**
* Promise 對象的 race 方法
* @param promises
* @returns {Promise}
*/
Promise.race = function (promises) {
return new Promise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
promises[i].then(
value => {
resolve(value);
},
reason => {
reject(reason);
}
);
}
});
};
// 17. 使用 setTimeOut 包裹代碼將其轉(zhuǎn)換為異步執(zhí)行的代碼
2.2 Class版本
class Promise {
/**
* 執(zhí)行多個(gè)回調(diào)的實(shí)現(xiàn)
* @param executor
* @constructor
*/
constructor(executor) {
// 3. 添加屬性
this.PromiseState = 'pending';
this.PromiseResult = null;
// 聲明一個(gè)屬性
this.callBacks = [];
// 預(yù)先保存this的值
const _this = this;
function resolve(data) {
// 修改對象的狀態(tài) PromiseState 屬于實(shí)例對象的屬性
// 設(shè)置結(jié)果值 PromiseResult 屬于實(shí)例對象的屬性
// 對象狀態(tài)只允許修改一次
if (_this.PromiseState !== 'pending') return;
_this.PromiseState = 'fullfilled';
_this.PromiseResult = data;
// 在異步任務(wù)的時(shí)候,調(diào)用回調(diào)函數(shù)的時(shí)機(jī)
// 循環(huán)遍歷執(zhí)行函數(shù)
setTimeout(() => {
_this.callBacks.forEach(item => {
item.onResolved(data);
});
});
}
function reject(data) {
// 對象狀態(tài)只允許修改一次
if (_this.PromiseState !== 'pending') return;
_this.PromiseState = 'rejected';
_this.PromiseResult = data;
// 在異步任務(wù)的時(shí)候讳窟,調(diào)用回調(diào)函數(shù)的時(shí)機(jī)
/**
* 循環(huán)遍歷執(zhí)行多個(gè)函數(shù)
*/
setTimeout(() => {
_this.callBacks.forEach(item => {
item.onRejected(data);
});
});
}
// 執(zhí)行器函數(shù)式同步調(diào)用的
// 調(diào)用執(zhí)行器函數(shù)
try {
executor(resolve, reject);
} catch (e) {
reject(e);
}
}
// then 方法
then(onResolved, onRejected) {
// 保存this的值
const _this = this;
// 判斷回調(diào)函數(shù)的參數(shù) 是否為一個(gè)函數(shù)類型
if (typeof onRejected !== 'function') {
/**
* 實(shí)現(xiàn)異常穿透的原理
* @param reason
*/
onRejected = reason => {
throw reason;
}
}
/**
* Promise實(shí)現(xiàn)值傳遞
*/
if (typeof onResolved !== 'function') {
onResolved = value => value;
}
return new Promise((resolve, reject) => {
function callBack(type) {
/**
* 當(dāng)出現(xiàn)異常的時(shí)候
*/
try {
// 獲取回調(diào)函數(shù)的執(zhí)行結(jié)果
let result = type(_this.PromiseResult);
if (result instanceof Promise) {
// 如果是Promise類型的對象
result.then(value => {
resolve(value);
},
reason => {
reject(reason);
})
} else {
// 返回非Promise對象的時(shí)候狀態(tài)為成功
resolve(result);
}
} catch (e) {
reject(e);
}
}
// 調(diào)用回調(diào)函數(shù) 成功失敗的狀態(tài)是 由PromiseState決定
if (this.PromiseState === 'fullfilled') {
setTimeout(() => {
callBack(onResolved);
});
}
if (this.PromiseState === 'rejected') {
setTimeout(() => {
callBack(onRejected);
});
}
// 判斷 pending的狀態(tài)
if (this.PromiseState === 'pending') {
// 回調(diào)的執(zhí)行時(shí)機(jī)让歼?
// 保存回調(diào)函數(shù) 重要 重要 重要
// 8. 實(shí)現(xiàn)執(zhí)行多個(gè)回調(diào)
this.callBacks.push({
onResolved: function () {
callBack(onResolved);
},
onRejected: function () {
callBack(onResolved);
}
});
}
});
}
// catch 方法
catch(onRejected) {
/**
* 直接調(diào)用then方法
*/
return this.then(undefined, onRejected);
}
/**
* Promise對象的 resolve 方法
* @param value
* @returns {Promise}
*/
static resolve(value) {
return new Promise((resolve, reject) => {
// 判斷value 的類型
if (value instanceof Promise) {
value.then(value => {
resolve(value);
}, reason => {
reject(reason);
});
} else {
resolve(value);
}
});
};
/**
* 為Promise對象添加 reject 方法
* @param reason
* @returns {Promise}
*/
static reject(reason) {
return new Promise((resolve, reject) => {
reject(reason);
});
};
/**
* 為Promise對象添加 all 方法
* @param promises
* @returns {Promise}
*/
static all(promises) {
// 定義一個(gè)計(jì)數(shù)變量 計(jì)算當(dāng)前Promise成功的個(gè)數(shù)
let count = 0;
let arr = [];
return new Promise((resolve, reject) => {
// 遍歷 promises
for (let i = 0; i < promises.length; i++) {
promises[i].then(value => {
// 得知對象的狀態(tài)是成功
// 狀態(tài)成功就會走到該函數(shù)中 進(jìn)行count++ 操作
count++;
// 將當(dāng)前promise對象成功的結(jié)果存到一個(gè)數(shù)組中保持原有的順序
arr[i] = value;
// 判斷
if (count === promises.length) {
// 修改此時(shí)Promise的狀態(tài)
resolve(arr);
}
},
reason => {
reject(reason);
});
}
});
};
/**
* Promise 對象的 race 方法
* @param promises
* @returns {Promise}
*/
static race(promises) {
return new Promise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
promises[i].then(
value => {
resolve(value);
},
reason => {
reject(reason);
}
);
}
});
};
}