實現一個複雜版本的promise
function PromiseA(executor){
this.status = 'pending'
// 成功value
this.value = undefined;
// 失敗reason
this.reason = undefined;
// 成功監聽陣列
this.succArr = [];
// 失敗監聽陣列
this.errArr = [];
var that = this;
function resolve(value){
if(that.status === 'pending'){
that.status = 'resolved';
that.value = value;
that.succArr.forEach(function (item) {
item(that.value)
})
}
}
function reject(reason) {
if(that.status === 'pending'){
that.status = 'rejected';
that.reason = reason;
that.errArr.forEach(function (item) {
item(that.reason)
})
}
}
try {
executor(resolve,reject)
}catch (e) {
reject(e)
}
}
PromiseA.prototype = {
constructor:PromiseA,
then:function (succFn,errFn) {
var that = this;
var Promise2;
// catch函式的核心
errFn = typeof errFn === 'function' ? errFn : function(err){ throw err}
Promise2 = new PromiseA(function(resolve,reject){
if(that.status === 'pending'){
that.succArr.push(function () {
// 解決非同步問題,其實promise屬於微任務,這裡用巨集任務模擬非同步
setTimeout(function () {
try{
// then函式的返回值會包裝成新的promise
var x = succFn(that.value);
that.resolvePromise(Promise2, x, resolve, reject);
}catch (e) {
reject(e);
}
},0)
});
that.errArr.push(function () {
setTimeout(function () {
try{
var x = errFn(that.reason);
that.resolvePromise(Promise2, x, resolve, reject);
}catch (e) {
reject(e);
}
},0)
})
}
/*
* 為什麼還要對status === 'resolved' 和 'rejected'做相關處理呢?
* 原因:如果promise內部是非同步函式,不做相關處理也是可以的,但是對於內部是同步程式碼時,當執行resolve時,sussArr還未push到then內部函式,導致無法執行then內部函式
*
* */
if(that.status === 'resolved'){
setTimeout(function () {
try{
// then函式的返回值會包裝成新的promise
var x = succFn(that.value);
that.resolvePromise(Promise2, x, resolve, reject);
}catch (e) {
reject(e);
}
},0)
}
if(that.status === 'rejected'){
setTimeout(function () {
try{
var x = errFn(that.reason);
that.resolvePromise(Promise2, x, resolve, reject);
}catch (e) {
reject(e);
}
},0)
}
});
return Promise2;
},
/*
* @desc:resolvePromise主要解決 then內部 return { then:function(){}} return function(){} ruturn new Promise情況
* @這是Promise規範中的方法,瞭解流程即可
* 其終極目的: resolve(data)
*
* 這個函式比較難理解
* */
resolvePromise: function(promise2, x, resolve, reject) {
let self = this;
let called = false; // called 防止多次呼叫
if (promise2 === x) {
return reject(new TypeError('迴圈引用'));
}
if (x !== null && (Object.prototype.toString.call(x) === '[object Object]' || Object.prototype.toString.call(x) === '[object Function]')) {
// x是物件或者函式
try {
let then = x.then;
if (typeof then === 'function') {
/*
* 難點:當返回是個promise時,但是確保其resolve reject要傳遞到我們寫的下一個then
*
* 1.當第一次呼叫then時,其實已經生成了一個新的promiseX
* 2.then內部函式又返回一個promiseY,但是在呼叫下一個then時,我們使用的時promiseX,那如何保證promiseY的resolve值傳遞到promiseX呢?
* 3.除非我們可以用promiseX的resolve(promiseY的resolve的值)
* 4.解決方法,內部呼叫then方法,將promiseY的resolve的值傳遞下來
* 5.遞迴處理
* 6.牢記我們這個函式的目的: promiseX的resolve(data)
* */
then.call(x, (y) => {
// 別人的Promise的then方法可能設定了getter等,使用called防止多次呼叫then方法
if (called) return ;
called = true;
// 成功值y有可能還是promise或者是具有then方法等,再次resolvePromise,直到成功值為基本型別或者非thenable
self.resolvePromise(promise2, y, resolve, reject);
}, (reason) => {
if (called) return ;
called = true;
reject(reason);
});
} else {
if (called) return ;
called = true;
resolve(x);
}
} catch (reason) {
if (called) return ;
called = true;
reject(reason);
}
} else {
// x是普通值,直接resolve
resolve(x);
}
},
//實現catch函式:promise鏈中所有錯誤都會捕獲
catch:function (errFn) {
// 其實只要給then內部的errFn新增一個預設函式即可,因為每次then都會向下傳遞,所有catch寫到後面即可捕獲到錯誤
return this.then(null,errFn)
}
}複製程式碼