JSでPromiseクラスを実現
8435 ワード
Promiseは、実はクラスであり、内部にはレギュレータが保存されており、レシオとレギュレータを露出することによって、対応するレギュレータ がトリガされる.内部には3つの状態値、pending、fulfiled、failed があります. Promiseは、マイクロタスクであり、settimoutなどのマクロタスクとは異なり、実行順序は、まずメインスレッドであり、その後、マイクロタスクであり、マクロタスク である.には以下の方法があります.then、catch、all、race、reolve、reject 一旦Promiseを実施したら、必ず実行します. を停止できません. Promiseでは、エラーが発生してもrejectが起動されますので、プログラムを中断することはありません. catchは実はthenのシンタックスキャンディーで、then(null,rej)に相当しています.つまり、空 に反転しました.もしthenにフィードバックが入っていないなら、promiseのresoveまたはrejectの値は次のthenまたはcatchに伝えられます.
内部変数 は、入ってきたfunc を同時に実行する. then方法は、プロトタイプチェーン上で定義され、プロミセ に戻る.分の3つの状態で処理し、pendingの場合はコールバックキューにのみフィードバックを追加し、同時にpromise化、fulfilledまたはfailedの場合は直接実行し、promise に戻ります.ここで、もしrecbやrejCbが空に入ったら、promiseのvalueを返してくれるpromise を見ました.、cbがpromiseに戻ると、そのpromiseのresまたはrejで上位層のresまたはrejがトリガされ、同時にcbのdata値は内部層promiseのvalue である.例は以下の通りです. は、プロミセのresoveまたはrejectがthenに対応するコールバック関数を入力しないと、そのvalueを継続的に伝達していきます.
タスク作成タイミング は、reloveおよびrejectの定義から見られますが、プロミスの状態とそのvalueだけを変えると、マイクロタスク、すなわち、非同期はどこで作成されますか?注意次の例は、 です.マイクロタスクは、thenにおいて作成され、then方法の定義に戻ると、handleResoliveおよびhandleRejectが見られ、queue Microtaskを利用してマイクロタスクを作成する が見られます.控訴例及びthen方法を組み合わせて見ることができます. p 1.thenがコールバック関数を登録し、彼をマイクロタスクに置いた後、promise に戻りました.が返されるpromiseのresoveは、現在実行されていないthenがpromiseに戻るというマイクロタスクに置かれています. ですので、戻りプロミスでthenに登録されたコールバック関数は、resoloveListに入れられています.つまり、現在は を実行していません.は次にp 2.thenに行き、また、1つのマイクロタスク を生成する.現在のマイクロタスクのキューには、p 1 thenの出力を先に実行し、p 1.thenを実行してpromiseのResoliveに戻り、また新たにマイクロタスクを作成し、マイクロタスクのキュー に置く.は次にp 2 then、p 1 then を実行する.
catchの方法 catch方法、すなわちthenのシンタックス糖 all方法は、一つの配列に入って、一つのpromiseを出力し、全部のresoveを出力する時、出力promise状態がfulfiledになり、resove をトリガします.いずれかが入ってきたプロモーションに失敗した場合、プロモーション状態がfailedとなり、rejectをトリガしますが、他のプロモーションの継続実行は中止されません.他のプロモーションの実行はpromise.allがpromiseに戻る状態には影響しません. race方法はpromise配列の中で最初の実行が完了した状態に戻り、fulfiledであれfailedであれ、配列中の残りのpromiseに影響を与えずに を実行し続ける.上記のdoneフラグは取り除かれます.promiseでは、resoveは2回しか実行されません.最初にthen関数を実行しただけです.具体的な理由は、第1回のresoveでは、resove Listのコールバック関数を実行した後、それをクリアします.即ち、実行が完了してから新たなthenメソッドを登録しないと、第2回のresoliveはフィードバックをトリガしません. また、第二回のresolove後に新たに登録されたthenであっても、その取得値は、第二回のresoveの値ではなく、プロミゼ内のvalueを更新すると、プロミセ状態からfulfiledまたはfailedに変化するときにのみ発生します.promiseの状態が固まります.例は以下の通りです. はraceに戻り、上記のrace方法は以下のように簡略化されています. reeveとrejectの方法は、内部定義を暴露するreloveとrejectで、promise に戻ります.
const p = new Promise((res) => res(2));
p.then(null).then((data) => console.log(data)); // 2
p.then(() => {}).then((data) => console.log(data)); // undefined
p.catch((data) => {console.log('catch', data)}).then((data) => console.log('then', data)); // then, 2
実現する内部変数
function myPromise(func) {
this.status = 'pending'; //
this.value = undefined; //
this.resolveList = []; //
this.rejectList = []; //
}
内部reolveとrejectの方法function myPromise(func) {
this.status = 'pending'; //
this.value = undefined; //
this.resolveList = []; //
this.rejectList = []; //
const resolve = (data) => {
if (this.status === 'pending') {
this.status = 'fulfiled';
this.value = data;
this.resolveList.forEach((resFunc) => resFunc(data));
this.resolveList.length = 0;
}
};
const reject = (data) => {
if (this.status === 'pending') {
this.status = 'failed';
this.value = data;
this.rejectList.forEach((resFunc) => resFunc(data));
this.rejectList.length = 0;
}
};
if (typeof func === 'function') {
try {
func(resolve, reject); // promise func , resolve reject
} catch(err) {
reject(err);
}
}
}
thenメソッドfunction handleResolve(res, rej, resCb, rejCb) {
queueMicrotask(() => {
// ,
try {
const ret = typeof resCb === 'function' ? resCb(this.value) : this.value;
if (ret instanceof myPromise) {
ret.then(res, rej);
} else {
res(ret);
}
} catch (err) {
rej(err);
}
});
}
function handleReject(res, rej, resCb, rejCb) {
queueMicrotask(() => {
// ,
try {
const ret = typeof rejCb === 'function' ? rejCb(this.value) : this.value;
if (ret instanceof myPromise) {
ret.then(res, rej);
} else {
rej(ret);
}
} catch (err) {
rej(err);
}
});
}
myPromise.prototype.then = function (resCb, rejCb) {
if (this.status === 'pending') {
return new myPromise((res, rej) => {
this.resolveList.push(() => {
handleResolve(res, rej, resCb, rejCb));
});
this.rejectList.push(() => {
handleReject(res, rej, resCb, rejCb));
});
});
} else if (this.status === 'fulfiled') {
return new myPromise((res, rej) => {
handleResolve(res, rej, resCb, rejCb));
});
} else if (this.status === 'failed') {
return new myPromise((res, rej) => {
handleReject(res, rej, resCb, rejCb));
});
}
};
const p1 = new Promise((res) => res(1));
p1.then(null).then((data) => console.log(data));
p1.then(() => {}).then((data) => console.log(data));
const p2 = new Promise((res, rej) => rej(2));
p2.then(() => {}).catch((err) => console.log(err));
p2.then(() => {}, () => {}).catch((err) => console.log(err));
/*
:
1
undefined
2
undefined
*/
タスク作成タイミング
const p = new Promise((res) => res(1));
p.then(() => console.log('p1 then'))
.then(() => console.log('p1 then then'));
const p2 = new Promise((res) => res(2));
p2.then(() => console.log('p2 then'));
/*
:p1 then -> p2 then -> p1 then then
*/
catchの方法
myPromise.prototype.catch = (rej) => myPromise.prototype.then(null, rej);
allメソッドmyPromise.prototype.all = (iterable) => {
const len = iterable.length;
const result = [];
return new myPromise((resolve, reject) => {
let index = 0;
iterable.forEach((item) => {
item.then((data) => {
result.push(data);
index++;
if (index === len) resolve(result);
}, (err) => {
reject(err);
});
});
});
}
raceメソッドmyPromise.prototype.race = (iterable) => {
let done = false;
return new myPromise((resolve, reject) => {
iterable.forEach((item) => {
item.then((data) => {
if (!done) {
done = true;
resolve(data);
}
}, (err) => {
if (!done) {
done = true;
reject(err);
}
});
});
});
}
const p1 = new Promise((res) => {
setTimeout(() => { console.log('res(1)'); res(1)}, 1000);
setTimeout(() => { console.log('res(2)'); res(2)}, 3000);
});
p1.then((data) => console.log(data));
setTimeout(() => {
console.log('then again');
p1.then((data) => console.log(data));
}, 5000);
/*
res(1) -> 1 -> res(2) -> then again -> 1
1 2
*/
myPromise.prototype.race = (iterable) => {
return new myPromise((resolve, reject) => {
iterable.forEach((item) => {
item.then(resolve, reject);
});
});
}
外部reolveとrejectの方法myPromise.prototype.reslove = (data) => {
return new myPromise((resolve, reject) => {
resolve(data);
});
};
myPromise.prototype.reject = (data) => {
return new myPromise((resolve, reject) => {
reject(data);
});
};