Promise練習三(手寫簡版promise + 符合promise/A+規範的promise)
阿新 • • 發佈:2020-12-19
技術標籤:好好學習javascript
JavaScript實現一個簡易的Promise物件
class MyPromise {
constructor(handleFn) {
// Promise的狀態
this.PromiseState = "pending";
// Promise的結果
this.PromiseValue = null;
// 畫重點!!!這兩個list是為了將下一個Promise與當前Promise相關聯
this._toNextResolve = [];
this._toNextReject = [];
// Promise接收的函式引數
handleFn(this.triggerResolve.bind(this), this.triggerReject.bind(this));
}
// resolve處理
triggerResolve(data) {
// 這裡使用setTimeout的原因是:確保resolve處理函式在handleFn所處的事件迴圈後以新的堆疊非同步執行。(參見:2.4)
setTimeout(() => {
// Promise的狀態不是pending時,不能被修改狀態。(參見:1)
if (this .PromiseState !== "pending") return;
// 修改Promise的狀態和值
this.PromiseState = "fullfilled";
this.PromiseValue = data;
this._toNextResolve.forEach(item => item(data));
this._toNextResolve = [];
}, 0);
}
// reject處理,同上
triggerReject(data) {
setTimeout (() => {
if (this.PromiseState !== "pending") return;
this.PromiseState = "rejected";
this.PromiseValue = data;
this._toNextReject.forEach(item => item(data));
this._toNextReject = [];
}, 0);
}
// 將多個Promise包裝成一個新的Promise例項,並在所有非同步操作執行完後執行回撥
static all(list) {
const result = [];
let count = 0;
return new MyPromise(function(resolve, reject) {
for (let [i, MyPromiseInstance] of list.entries()) {
MyPromiseInstance.then(
res => {
result[i] = res;
count++;
if (count === list.length) resolve(result);
},
err => {
reject(err);
}
);
}
});
}
static race(list) {
return new MyPromise(function(resolve, reject) {
for (let [i, MyPromiseInstance] of list.entries()) {
MyPromiseInstance.then(
res => {
resolve(res);
},
err => {
reject(err);
}
);
}
});
}
// static resolve(val) {
// return new MyPromise(resolve=>resolve(val));
// }
// static reject(val) {
// return new MyPromise(resolve=>{}, reject=>reject(val));
// }
}
MyPromise.prototype.then = function(onResolved, onRejected) {
const { PromiseState, PromiseValue } = this;
// then方法必須返回一個Promise物件。(參見:2.7)
return new MyPromise((onNextResolved, onNextRejected) => {
function onLastFullFilled(val) {
if (typeof onResolved !== "function") {
// 如果onResolved不是函式,則需要忽略他。(參見:2.1)
onNextResolved(val);
} else {
const res = onResolved(val);
if (res && typeof res.then === "function") {
// 若res 是一個 promise
res.then(onNextResolved);
} else {
// 若res 非 promise,則直接執行下一個 onNextResolved
onNextResolved(res);
}
}
}
function onLastRejected(val) {
if (typeof onRejected !== "function") {
// 如果onRejected不是函式,則需要忽略他。(參見:2.1)
onNextRejected(val);
} else {
const res = onRejected(val);
if (res && typeof res.then === "function") {
// 若res 是一個 promise
res.then(onNextRejected);
} else {
// 若res 非 promise,則直接執行下一個 onNextRejected
onNextRejected(res);
}
}
}
switch (PromiseState) {
case "pending": {
this._toNextResolve.push(onLastFullFilled);
this._toNextReject.push(onLastRejected);
break;
}
}
});
};
const p1 = function() {
return new MyPromise(function(resolve, reject) {
setTimeout(function() {
const random = Math.random() * 10;
if (random > 5) {
resolve("已完成1:" + random);
} else {
reject("已拒絕1:" + random);
}
}, 1000);
});
};
const p2 = function() {
return new MyPromise(function(resolve, reject) {
const random = Math.random() * 10;
resolve("已完成2:" + random);
});
};
const p3 = function() {
return new MyPromise(function(resolve, reject) {
const random = Math.random() * 10;
resolve("已完成3:" + random);
});
};
p1().then(
res => {
console.log('then方法:');
console.log(res);
},
err => {
console.log('then方法:');
console.log(err);
}
);
MyPromise.all([p1(), p2(), p3()]).then(
res => {
console.log('all方法:');
console.log(res);
},
err => {
console.log('all方法:');
console.log(err);
}
);
MyPromise.race([p1(), p2(), p3()]).then(
res => {
console.log('race方法:');
console.log(res);
},
err => {
console.log('race方法:');
console.log(err);
}
);
按照Promise/A+規範,使用typescript實現Promise
// Promise/A+ 1.3: "value" is any legal JavaScript Value.
type PromiseValue = any;
// Promise/A+ 1.5: "reason" is a value that indicates why a promise was rejected.
type PromiseReason = any;
// Promise/A+ 2.2.1: Both onFulfilled and onRejected are optional arguments.
type onFulfilled = any;
type onRejected = any;
// Promise/A+ 2.1: A Promise must be in one of three states: pending, fulfilled, or rejected.
enum PromiseState {
Pending = "pending",
Fulfilled = "fulfilled",
Rejected = "rejected"
}
type resolve = (value: PromiseValue) => void;
type reject = (reason: any) => void;
type executor = (resolve: resolve, reject: reject) => void;
// 型別判斷
const ToString = Object.prototype.toString;
const IsFunctionType = (val: any): boolean => ToString.call(val) === "[object Function]";
const IsObjectType = (val: any): boolean => ToString.call(val) === "[object Object]";
const IsNullType = (val: any): boolean => ToString.call(val) === "[object Null]";
const IsPromiseType = (val: any): boolean => val instanceof PromiseImplement;
const hasAttribute = (target: any, name: string): boolean => name in target;
const promiseProcedure = function(promise: PromiseImplement, x: any, resolve: resolve, reject: reject){
// Promise/A+ 2.3.1: If promise and x refer to the same object, reject promise with a TypeError as the reason.
if(promise == x){
const err = new TypeError('Endless loop detected for Promise');
return reject(err);
}
// Promise/A+ 2.3.2: If x is a promise, adopt its state
if(IsPromiseType(x)){
if(x.state == PromiseState.Pending){
// Promise/A+ 2.3.2.1: If x is pending, promise must remain pending until x is fullfilled or rejected.
x.then((val: PromiseValue) => promiseProcedure(promise, val, resolve, reject), reject);
}else{
// Promise/A+ 2.3.2.2: If/when x is fulfilled, fulfill promise with the same value.
// Promise/A+ 2.3.2.3 If/when x is rejected, reject promise with the same reason.
x.then(resolve, reject);
}
}else if(IsObjectType(x) || IsFunctionType(x)){
let ignored: boolean = false;
// Promise/A+ 2.3.3: If x is an Object or funciton
try{
// Promise/A+ 2.3.3.1: let then be x.then.
let then = x.then;
if(IsFunctionType(then)){
const resolvePromise = (y: any) => {
// Promise/A+ 2.3.3.3.1: If/when resolvePromise is called with a value y, run[[Resolve]](promise, y)
if(ignored) return;
ignored = true;
promiseProcedure(x, y, resolve, reject);
}
const rejectPromise = (r: any) => {
// Promise/A+ 2.3.3.3.2: If/when rejectPromise is called with a reason r, reject promise with r.
if(ignored) return;
ignored = true;
return reject(r);
}
// Promise/A+ 2.3.3.3: If then is a function,call it with x as this, first argument resolvePromise, and second argument rejectPromsie
then(resolvePromise, rejectPromise).bind(x);
}else {
// Promise/A+ 2.3.3.4: If then is not a function, fulfill promise with x.
resolve(x);
}
}catch(e){
if(ignored) return;
// Promise/A+ 2.3.3.2: if retrieving the property x.then results in a thrown exception e, reject promise with e as the reason.
reject(e);
}
}else {
// Promise/A+ 2.3.4: If x is not an object or function, fulfill promise with x
resolve(x);
}
}
class PromiseImplement {
state: PromiseState;
value: PromiseValue;
reason: PromiseReason;
onFulfilledCallbacks: onFulfilled[] = [];
onRejectedCallbacks: onRejected[] = [];
constructor(executor: executor) {
this.state = PromiseState.Pending;
if (!IsFunctionType(executor)) {
throw new TypeError(`Promise resolver ${executor} is not a function`);
}
if (!(this instanceof PromiseImplement)) {
throw new TypeError("undefined is not a promise");
}
executor(this.triggerResolve.bind(this), this.triggerReject.bind(this));
}
triggerResolve(val) {
setTimeout(()=>{
if(this.state !== PromiseState.Pending) return;
this.state = PromiseState.Fulfilled;
this.value = val;
this.onFulfilledCallbacks.forEach((onFulfilled: onFulfilled)=>onFulfilled());
this.onFulfilledCallbacks = [];
}, 0);
}
triggerReject(reason) {
setTimeout(()=>{
if(this.state !== PromiseState.Pending) return;
this.state = PromiseState.Rejected;
this.reason = reason;
this.onRejectedCallbacks.forEach((onRejected: onRejected)=>onRejected());
this.onRejectedCallbacks = [];
},0)
}
// Promise/A+ 2.2: A promise must provide a then method to access its current or eventual valur or reason.
// Promise/A+ 2.2: A promise's then method accepts two arguments: onFulfilled, onRejected
then(onFulfilled?: onFulfilled, onRejected?: onRejected) {
// Promise/A+ 2.2.1.1: If onFulfilled is not a function, it must be ignored.
// Promise/A+ 2.2.5: onFulfilled must be called as function
!IsFunctionType(onFulfilled) &&
(onFulfilled = (PromiseValue: PromiseValue) => PromiseValue);
// Promise/A+ 2.2.1.2: If onRejected is not a function,it must be ignored.
// Promise/A+ 2.2.5: onRejected must be called as function
!IsFunctionType(onRejected) &&
(onRejected = (PromiseReason: PromiseReason) => PromiseReason);
// Promise/A+ 2.2.7: then must return a promise.
const promise2 = new PromiseImplement((resolve, reject)=>{
if(this.state == PromiseState.Pending){
// Promise/A+ 2.2.2: If onFulfilled is a function, it must be called after promise is fulfilled,with promise's value as its first argument.
this.onFulfilledCallbacks.push(()=>{
const x = onFulfilled(this.value);
promiseProcedure(this, x, resolve, reject);
});
// Promise/A+ 2.2.3: If onRejected is a function, it must be called after promise is rejected,with promise's reason as its first argument.
this.onRejectedCallbacks.push(()=>{
const x = onRejected(this.reason);
promiseProcedure(this, x, resolve, reject);
});
}
});
return promise2;
}
}
const p1 = function() {
return new PromiseImplement(function(resolve, reject) {
setTimeout(function() {
const random = Math.random() * 10;
if (random > 5) {
resolve("已完成1:" + random);
} else {
reject("已拒絕1:" + random);
}
}, 1000);
});
};
p1().then((res)=>{
console.log(res)
}, (err)=>{
console.log(err);
}).then((res)=>{
console.log('完成2:' + res);
},(err)=>{
console.log('拒絕3:' + err);
});