1.代碼實現
// promise 三個狀態
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED ='rejected';
class Promisex {
constructor(fn){
if(typeof fn !== 'function') throw new Error('Promisex must accept a function')
this.state = PENDING;//初始狀態
this.value = null;//返回的信息/拒絕的原因
this.onFulfilledQueues = [];//存儲fulfilled狀態對應onFulfilled函數隊列
this.onRejectedQueues = [];//存儲rejected狀態對應onRejected函數隊列
// value成功態時接收的終值
const resolve=(value) => {
if(value instanceof Promisex){
return value.then(resolve,reject);
}else{
//加setTimeout原因:
//實踐中要確保 onFulfilled 和 onRejected 方法異步執行,
setTimeout(() => {
if(this.state!==PENDING) return;
this.state = FULFILLED;
this.value = value;
let cb;
while (cb = this.onFulfilledQueues.shift()) {
cb(value)
}
});
}
}
// reason失敗態時接收的拒因
const reject=(reason)=>{
setTimeout(() => {
if(this.state!==PENDING) return;
this.state = REJECTED;
this.value = reason;
let cb;
while (cb = this.onRejectedQueues.shift()) {
cb(reason)
}
});
}
//執行
try {
fn(resolve,reject)
} catch (error) {
reject(error)
}
}
//註冊fulfilled狀態/rejected狀態對應的回調函數
//then返回一個新的promise對象
//then中需要處理三種狀態
then(onFulfilled, onRejected){
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason; };
let newPromise;
if(this.state===FULFILLED){//成功狀態
return newPromise=new Promisex((resolve,reject)=>{
setTimeout(() => {
try {
let x=onFulfilled(this.value);//onFulfilled有返回值
resolvePromise(newPromise,x,resolve,reject);
} catch (e) {
reject(e)
}
});
})
}
if(this.state===REJECTED){//失敗狀態
return newPromise=new Promisex((resolve,reject)=>{
setTimeout(() => {
try {
//不論 promise1 被 reject 還是被 resolve 時 promise2 都會被 resolve,只有出現異常時纔會被 rejected。
let x=onRejected(this.value);//onRejected有返回值
resolvePromise(newPromise,x,resolve,reject);
} catch (e) {
reject(e)
}
});
})
}
if(this.state===PENDING){//等待狀態
//將onFulfilled/onRejected收集暫存到隊列中
return newPromise=new Promisex((resolve,reject)=>{
this.onFulfilledQueues.push((value)=>{
try {
let x=onFulfilled(value);
resolvePromise(newPromise,x,resolve,reject);
} catch (e) {
reject(e)
}
})
this.onRejectedQueues.push((reason)=>{
try {
let x=onRejected(reason);
resolvePromise(newPromise,x,resolve,reject);
} catch (e) {
reject(e);
}
})
})
}
}
catch(onRejected){
return this.then(undefined,onRejected);
}
finally(cb){
return this.then(value=>{
Promisex.resolve(cb()).then(()=>value)
},err=>{
Promisex.resolve(cb()).then(()=>{throw err})
})
}
static resolve(value){
if(value instanceof Promisex) return value;
return new Promisex(resolve=>resolve(value));
}
static reject(reason){
return new Promisex((undefined,reject)=>reject(reason));
}
static all(list){
return new Promisex((resolve,reject)=>{
let values=[];
let count=0;
for(let [i,p] of list.entries()){
this.resolve(p).then(res=>{
values[i]=res;
count++;
if(count === list.length) resolve(values);
},err=>{
reject(err);
})
}
})
}
static race(list){
return new Promisex((resolve,reject)=>{
for(let p of list){
this.resolve(p).then(res=>{
resolve(res);
},err=>{
reject(err);
})
}
})
}
}
function resolvePromise(promise2,x,resolve,reject){
if(promise2===x){// 如果從onFulfilled中返回的x 就是promise2 就會導致循環引用報錯
return reject(new TypeError('循環引用'))
}
let called = false;// 避免多次調用
// * 1.promise對象
// * 2.thenable對象/函數
// * 3.普通值
if(x instanceof Promisex){
if(x.state===PENDING){
x.then(y=>{
resolvePromise(promise2,y,resolve,reject);
},reason=>{
reject(reason)
})
}else{
x.then(resolve,reject);
}
}else if(x&&(typeof x==='object'||typeof x==='function')){
try {
//是否是thenable對象(具有then方法的對象/函數)
let then = x.then;
if(typeof then==='function'){
then.call(x,y=>{
if(called)return;
called=true;
resolvePromise(promise2,y,resolve,reject);
},reason=>{
if(called)return;
called=true;
reject(reason);
})
}else{//說明是一個普通對象
resolve(x)
}
} catch (e) {
if(called)return;
called=true;
reject(e)
}
}else{
resolve(x);
}
}
//使用promises-aplus-tests測試
Promisex.deferred = function() {
// 延遲對象
let defer = {};
defer.promise = new Promisex((resolve, reject) => {
defer.resolve = resolve;
defer.reject = reject;
});
return defer;
};
module.exports = Promisex;
最後執行promises-aplus-tests ./xxx.js進行測試
2.例子
//不論 promise1 被 reject 還是被 resolve 時 promise2 都會被 resolve,只有出現異常時纔會被 rejected。
new Promisex((resolve, reject)=>{
setTimeout(() => {
reject(2)
}, 2000);
}).then(res=>{
console.log(res);
return res+1;
},e=>{
return e;//2
}).then(res=>{
console.log(res)//2
},e=>{
return e;
})
3.參考
Promise詳解與實現(Promise/A+規範)
Promises/A+規範(英文)
【翻譯】Promises/A+規範
Javascript異步編程的4種方法