一疗锐、Promise源碼實現(xiàn)
- 定義初始類型
// 三種狀態(tài)類型
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
// 新建兩個數(shù)組, 來分別存儲成功和失敗的回調(diào), 調(diào)用then的時候, 如果還是pending就存入數(shù)組.
FULFILLED_CALLBACK_LIST = [];
REJECTED_CALLBACK_LIST = [];
_status = PENDING;
- 設(shè)置初始狀態(tài)
// 入?yún)⑹且粋€函數(shù), 函數(shù)接收resolve和reject兩個參數(shù).
constructor(fn) {
// 設(shè)置初始狀態(tài)為pending
this.status = PENDING;
this.value = null;
this.reason = null;
// 在初始化promise的時候, 就要同步執(zhí)行這個函數(shù), 并且有任何報錯都要通過reject拋出去
try {
// 將resolve愉豺、reject的this指向當前執(zhí)行環(huán)境
fn(this.resolve.bind(this), this.reject.bind(this));
} catch (e) {
this.reject(e);
}
}
- 監(jiān)聽狀態(tài)變化
get status() {
return this._status;
}
// 在status發(fā)生變化的時候, 就執(zhí)行所有的回調(diào)
set status(newStatus) {
this._status = newStatus;
switch (newStatus) {
case FULFILLED: {
this.FULFILLED_CALLBACK_LIST.forEach(callback => {
callback(this.value);
});
break;
}
case REJECTED: {
this.REJECTED_CALLBACK_LIST.forEach(callback => {
callback(this.reason);
});
break;
}
}
}
- resolve 和 reject 方法允乐,這兩個方法是要更改status的, 入?yún)⒎謩e是value 和 reason. 從pending改到fulfilled/rejected.
resolve(value) {
if (this.status === PENDING) {
this.value = value;
this.status = FULFILLED;
}
}
reject(reason) {
if (this.status === PENDING) {
this.reason = reason;
this.status = REJECTED;
}
}
- 定義then方法
// then接收兩個參數(shù), onFulfilled 和 onRejected
then(onFulfilled, onRejected) {
// 檢查并處理參數(shù), 之前提到的如果不是function, 就原樣返回value或者reason.
const fulFilledFn = this.isFunction(onFulfilled) ? onFulfilled : (value) => {
return value;
}
const rejectedFn = this.isFunction(onRejected) ? onRejected : (reason) => {
throw reason;
};
// 如果 onFulfilled 不是函數(shù)且 promise1 成功執(zhí)行矮嫉, promise2 必須成功執(zhí)行并返回相同的值
const fulFilledFnWithCatch = (resolve, reject, newPromise) => {
// onFulfilled 和 onRejected 是微任務(wù),咱們可以用queueMicrotask包裹執(zhí)行函數(shù)
queueMicrotask(() => {
try {
if (!this.isFunction(onFulfilled)) {
resolve(this.value);
} else {
// 如果 onFulfilled 或者 onRejected 返回一個值 x 喳篇,則運行resolvePromise方法
const x = fulFilledFn(this.value);
this.resolvePromise(newPromise, x, resolve, reject);
}
// onFulfilled 或者 onRejected 拋出一個異常 e 敞临,則 promise2 必須拒絕執(zhí)行,并返回拒因 e麸澜。
} catch (e) {
reject(e)
}
})
};
// 如果 onRejected 不是函數(shù)且 promise1 拒絕執(zhí)行挺尿, promise2 必須拒絕執(zhí)行并返回相同的據(jù)因。
// 如果promise1的onRejected執(zhí)行成功了炊邦,promise2應(yīng)該被resolve
const rejectedFnWithCatch = (resolve, reject, newPromise) => {
queueMicrotask(() => {
try {
if (!this.isFunction(onRejected)) {
reject(this.reason);
} else {
const x = rejectedFn(this.reason);
this.resolvePromise(newPromise, x, resolve, reject);
}
} catch (e) {
reject(e);
}
})
}
// 根據(jù)當前promise的狀態(tài), 調(diào)用不同的函數(shù)编矾,then的返回值是一個promise
switch (this.status) {
case FULFILLED: {
const newPromise = new MPromise((resolve, reject) => fulFilledFnWithCatch(resolve, reject, newPromise));
return newPromise;
}
case REJECTED: {
const newPromise = new MPromise((resolve, reject) => rejectedFnWithCatch(resolve, reject, newPromise));
return newPromise;
}
case PENDING: {
const newPromise = new MPromise((resolve, reject) => {
this.FULFILLED_CALLBACK_LIST.push(() => fulFilledFnWithCatch(resolve, reject, newPromise));
this.REJECTED_CALLBACK_LIST.push(() => rejectedFnWithCatch(resolve, reject, newPromise));
});
return newPromise;
}
}
}
- 實現(xiàn)resolvePromise
resolvePromise(newPromise, x, resolve, reject) {
// 如果 newPromise 和 x 指向同一對象,以 TypeError 為據(jù)因拒絕執(zhí)行 newPromise
// 這是為了防止死循環(huán)
if (newPromise === x) {
return reject(new TypeError('The promise and the return value are the same'));
}
if (x instanceof MPromise) {
// 如果 x 為 Promise 馁害,則使 newPromise 接受 x 的狀態(tài)
// 也就是繼續(xù)執(zhí)行x窄俏,如果執(zhí)行的時候拿到一個y,還要繼續(xù)解析y
x.then((y) => {
this.resolvePromise(newPromise, y, resolve, reject);
}, reject);
} else if (typeof x === 'object' || this.isFunction(x)) {
// 如果 x 為對象或者函數(shù)
if (x === null) {
// null也會被判斷為對象
return resolve(x);
}
let then = null;
try {
// 把 x.then 賦值給 then
then = x.then;
} catch (error) {
// 如果取 x.then 的值時拋出錯誤 e 碘菜,則以 e 為據(jù)因拒絕 promise
return reject(error);
}
// 如果 then 是函數(shù)
if (this.isFunction(then)) {
let called = false;
// 將 x 作為函數(shù)的作用域 this 調(diào)用
// 傳遞兩個回調(diào)函數(shù)作為參數(shù)凹蜈,第一個參數(shù)叫做 resolvePromise ,第二個參數(shù)叫做 rejectPromise
try {
then.call(
x,
// 如果 resolvePromise 以值 y 為參數(shù)被調(diào)用忍啸,則運行 resolvePromise
(y) => {
// 需要有一個變量called來保證只調(diào)用一次.
if (called) return;
called = true;
this.resolvePromise(newPromise, y, resolve, reject);
},
// 如果 rejectPromise 以據(jù)因 r 為參數(shù)被調(diào)用仰坦,則以據(jù)因 r 拒絕 promise
(r) => {
if (called) return;
called = true;
reject(r);
});
} catch (error) {
// 如果調(diào)用 then 方法拋出了異常 e:
if (called) return;
// 否則以 e 為據(jù)因拒絕 promise
reject(error);
}
} else {
// 如果 then 不是函數(shù),以 x 為參數(shù)執(zhí)行 promise
resolve(x);
}
} else {
// 如果 x 不為對象或者函數(shù)计雌,以 x 為參數(shù)執(zhí)行 promise
resolve(x);
}
}
- 其他方法定義
// catch方法悄晃,使得可以調(diào)用.catch
catch (onRejected) {
return this.then(null, onRejected);
}
isFunction(param) {
return typeof param === 'function';
}
- 靜態(tài)方法實現(xiàn)
// 將現(xiàn)有對象轉(zhuǎn)為Promise對象
static resolve(value) {
if (value instanceof MPromise) {
return value;
}
// 如果 Promise.resolve 方法的參數(shù),不是具有 then 方法的對象(又稱 thenable 對象)凿滤,則返回一個新的 Promise 對象妈橄,且它的狀態(tài)為fulfilled庶近。
return new MPromise((resolve) => {
resolve(value);
});
}
static reject(reason) {
// 返回一個新的Promise實例,該實例的狀態(tài)為rejected眷蚓。Promise.reject方法的參數(shù)reason鼻种,會被傳遞給實例的回調(diào)函數(shù)。
return new MPromise((resolve, reject) => {
reject(reason);
});
}
`const p = Promise.race([p1, p2, p3]);`
// 該方法是將多個 Promise 實例溪椎,包裝成一個新的 Promise 實例普舆。
// 只要p1、p2校读、p3之中有一個實例率先改變狀態(tài),p的狀態(tài)就跟著改變祖能。那個率先改變的 Promise 實例的返回值歉秫,就傳遞給p的回調(diào)函數(shù)。
static race(promiseList) {
return new MPromise((resolve, reject) => {
const length = promiseList.length;
if (length === 0) {
return resolve();
} else {
for (let i = 0; i < length; i++) {
MPromise.resolve(promiseList[i]).then(
(value) => {
return resolve(value);
},
(reason) => {
return reject(reason);
});
}
}
});
}
- 測試代碼
const test = new MPromise((resolve, reject) => {
setTimeout(() => {
resolve(111);
}, 1000);
}).then(console.log); // 111
console.log(test);
// MPromise {
// FULFILLED_CALLBACK_LIST: [],
// REJECTED_CALLBACK_LIST: [],
// _status: 'pending',
// value: null,
// reason: null
// }
setTimeout(() => {
console.log(test);
// MPromise {
// FULFILLED_CALLBACK_LIST: [],
// REJECTED_CALLBACK_LIST: [],
// _status: 'fulfilled',
// value: undefined,
// reason: null
// }
}, 2000)
二养铸、迭代器和生成器
迭代器Iterator
迭代器Iterator 是 ES6 引入的一種新的遍歷機制雁芙,同時也是一種特殊對象,它具有一些專門為迭代過程設(shè)計的專有接口钞螟。
每個迭代器對象都有一個next()方法兔甘,每次調(diào)用都返回一個當前結(jié)果對象。當前結(jié)果對象中有兩個屬性:
- value:當前屬性的值
- done:用于判斷是否遍歷結(jié)束鳞滨,當沒有更多可返回的數(shù)據(jù)時洞焙,返回true
每調(diào)用一次next()方法,都會返回下一個可用的值拯啦,直到遍歷結(jié)束澡匪。
生成器generator
生成器是一種返回迭代器的函數(shù),通過function關(guān)鍵字后的星號(*)來表示褒链,函數(shù)中會用到新的關(guān)鍵字yield唁情。星號可以緊挨著function關(guān)鍵字,也可以在中間添加一個空格.
- 每當執(zhí)行完一條yield語句后函數(shù)就會自動停止執(zhí)行, 直到再次調(diào)用next();
- yield關(guān)鍵字只可在生成器內(nèi)部使用甫匹,在其他地方使用會導致程序拋出錯誤;
- 可以通過函數(shù)表達式來創(chuàng)建生成器, 但是不能使用箭頭函數(shù)
let generator = function *(){}
function* generator() {
const list = [1, 2, 3];
for (let i of list) {
yield i;
}
}
let g = generator();
console.log(g.next()); // {value: 1, done: false}
console.log(g.next()); // {value: 2, done: false}
console.log(g.next()); // {value: 3, done: false}
console.log(g.next()); // {value: undefined, done: true}
三甸鸟、利用Async封裝一個函數(shù), 能夠讓generator自動執(zhí)行到完畢
function longTimeFn(time) {
return new Promise(resolve => {
setTimeout(() => {
resolve(time);
}, time);
})
};
function asyncFunc(generator) {
const iterator = generator(); // 接下來要執(zhí)行next
// data為第一次執(zhí)行之后的返回結(jié)果,用于傳給第二次執(zhí)行
const next = (data) => {
const {
value,
done
} = iterator.next(data); // 第二次執(zhí)行兵迅,并接收第一次的請求結(jié)果 value 和 done
if (done) return; // 執(zhí)行完畢, 直接返回
// 第一次執(zhí)行next時抢韭,yield返回的 promise實例 賦值給了 value
value.then(data => {
next(data); // 當?shù)谝淮蝪alue 執(zhí)行完畢且成功時,執(zhí)行下一步(并把第一次的結(jié)果傳遞下一步)
});
}
next();
};
asyncFunc(function* () {
let data = yield longTimeFn(1000);
console.log(data);
data = yield longTimeFn(2000);
console.log(data);
return data;
})