非同期がわかりません。
「この関数はPromiseを返します」
Promiseって何だよ。
非同期やってもコールバック地獄にならないらしいけど、
結局引数に渡すのはコールバックだし、
僕からするとまーまーわからんのです。
というわけでPromiseを自作して雰囲気を掴むことにしました。
あくまで雰囲気を掴むだけです。実際の実装は微塵も知りません。
いろんなドキュメントを読んだ上での筆者の解釈(を筆者の実装レベルでできるとこだけ再現したもの)です。とんちんかんかもしれません。
妥協しまくりです。
Promise.allとかやりません。それどころかPromiseチェーンすらできません。
catchは実装したけど試してません。
(要はほとんどできてないじゃないか)
あまりにもとんちんかんなこと言ってたら教えて下さい。
信用しないでください。
レベル不足でクソコードです。
とりあえずよく呼び出す側のコードです。
<html lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>オレオレPromise</title>
<script type="text/javascript" src="promise.js"></script>
<script type="text/javascript" src="index.js"></script>
</head>
<body>
hello async
<button onclick="wait()">
async start
</button>
<p id="start" style="display:none"></p>
<p id="async" style="display:none"></p>
<p id="finish" style="display:none"></p>
</body>
</html>
const wait = ()=>{
const start = document.getElementById('start');
start.style="";
start.innerHTML += "start:" + new Date();
hidouki().then(finish).catch(dispError);
}
const hidouki = ()=>{
return new Promise(wait1);
//return new oreorePromise(wait1); //こっちに書き換えても動くようになるのが目標
}
const wait1 = (resolve,reject) => {
setTimeout(() => {
const async1 = document.getElementById('async');
async1.style="";
async1.innerHTML += "wait3 sec :" + new Date();
resolve('succeeded async');
},3000);
}
const finish = (value) => {
setTimeout(()=>{
const finish = document.getElementById('finish');
finish.style = "";
finish.innerHTML += value + " :" + new Date();
},5000);
}
const dispError = (error) => {
alert(error);
}
さて、new Promiseしているところを自作Promiseに書き換えても動くようにするのが目標です。
正直、Promiseのコンストラクタの引数はコールバック関数で、そのコールバック関数の引数はresolveとrejectで……、
って言われてわかる? みんなすごいね! 僕はわかんないです。そのresolveとrejectはどこから来たんだよ。
ってのを試行錯誤しながら理解しようとした結果、以下のようになりました。
class oreorePromise{
constructor(callback){
this.value = null;
this.state = 'pending';
this.thenTimer = [];
this.catchTimer = [];
callback(this.resolve.bind(this),this.reject.bind(this));
}
resolve(value){
this.value = value;
this.state ='fulfilled';
}
reject(value){
this.value = value;
this.state='rejected';
}
then(nextCallback){
this.thenTimer.push(setInterval(function(){
if(this.state === 'fulfilled'){
this.value = nextCallback(this.value);
clearInterval(this.thenTimer.shift());
}
else if(this.state === 'rejected'){
//待機やめる
clearInterval(this.thenTimer.shift());
}
}.bind(this),100)); //テストだし100ミリ秒くらい待とう
// https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Using_promises#Chaining
// then 関数は元の Promise とは別の新しい Promise を返します。
// らしいけど、もう力尽きた…。
return this;
}
catch(errorCallback){
this.catchTimer.push(setInterval(function(){
if(this.state === 'fulfilled'){
//待機やめる
clearInterval(this.catchTimer.shift())
}
else if(this.state === 'rejected'){
errorCallback(this.value);
clearInterval(this.catchTimer.shift())
}
}.bind(this),100));
}
}
JavaScriptのthisがわかんなくて、ただでさえわからないのにコールバックしまくってthisが迷子になって結構しんどかった。一応動いたけど。
Promiseよりthisの方が難しかった。
だれかthis教えて。
resolveとrejectはPromiseオブジェクトが持つメソッドで、
「コンストラクタに引数として渡したコールバック関数を実行するときに、resolveとrejectを引数として実行しますよ。
なので、実行したい関数を定義するときにresolveとrejectが引数になるように定義してくださいね」
というお約束だということが自分で書いてようやくわかった。
わかってからだと、ちゃんと書いてあることがわかる。なぜ最初から見つけられないのだ。
どこかの解説記事で、
「resolve関数を実行するとthenメソッドが呼び出される」
とか書いてあって腑に落ちなかったんだけど、たぶん、正しいのは、
「thenメソッドは即座に呼ばれていて、オブジェクトの持つ状態(oreorePromiseではプロパティstate)がfulfilledになるまで、引数として渡されたコールバック関数の実行を待つ」
なんだと思う。
(自信はない)
resolveは状態を変更しているだけで、別にthenに渡された関数を実行しているとかはない、と思う。(ここは特に自信ない)
最初にそう言う説明を見てしまったのでthenメソッドが魔法のように見えてしまっていたのだけれど、
(MDNで「さあ魔法の時間です。」とか言ってるしね!)
よく見ればobject.method()という至極基本的な文法だし、即座に呼ばれて当然だよなと。
このコードでは、状態が変わるまでsetIntervalでループしてるのは最高にダサいと思うけど、本質とずれるのであまり凝ってない。
const hidouki = ()=>{
//return new Promise(wait1);
return new oreorePromise(wait1); //こっちに書き換えても動くようになるのが目標
}
動いた。
ドキュメントを見返して、ソースを見返す度に「あ、ここ違うな」ってなる。
ただ、重ね重ねだけど、雰囲気を掴んでPromiseを使えるようになるのが目的で、Promiseのコピーを作りたいとかではない。
せめてPromiseチェーンは実装したかったけど、費用対効果が薄すぎてやめた。
個人的には
resolveとrejectはPromiseオブジェクトが持つメソッドで、
「コンストラクタに引数として渡したコールバック関数を実行するときに、resolveとrejectを引数として実行しますよ。
なので、実行したい関数を定義するときにresolveとrejectが引数になるように定義してくださいね」
というお約束だということが自分で書いてようやくわかった。どこかの解説記事で、
「resolve関数を実行するとthenメソッドが呼び出される」
とか書いてあって腑に落ちなかったんだけど、たぶん、正しいのは、
「thenメソッドは即座に呼ばれていて、オブジェクトの持つ状態(oreorePromiseでは変数名state)がfulfilledになるまで、引数として渡されたコールバック関数の実行を待つ」
なんだと思う。
(自信はない)
ここが理解できた時点でもういいかって感じ。おまけで完成させた。
みんな、async/await使おうぜ!
だれかthis教えて
Crieitは誰でも投稿できるサービスです。 是非記事の投稿をお願いします。どんな軽い内容でも投稿できます。
また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!
こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください。
ボードとは?
コメント