【JavaScript】Promiseでボタンクリックなどの不定期繰り返し処理を行う

更新日:2024/01/18

Promiseを使ってボタンクリックを処理する方法を紹介します。

 

ボタンクリックはPromiseを使用できない

ブラウザでボタンが押された時の処理をPromiseで行うのは簡単に思えます。
しかし実際にやってみると、できません。

次のコードは、2回目以降のボタンクリックに反応しません。

document.addEventListener("DOMContentLoaded",()=>{
  let count = 1;

  new Promise( (resolve)=>{
    document.getElementById("btn").addEventListener( "click" ,()=>resolve() );
  }).then( ()=>{
    console.log( count + "回目のclick" );
  }).finally(()=>{
    count ++;
  });

});

Promiseは非同期での一連の流れをシングルスレッドで同期させるオブジェクトです。
しかし流れを遡ることができません。
頭から再実行もできません。
したがって、Promiseを繰り返し使用することができないのです。

 

Promiseでボタンクリックを処理する

Promiseで不定期でのボタンクリックを処理したいときは、ボタンクリック後にPromiseを構築します。

document.addEventListener("DOMContentLoaded",()=>{
  let count = 0;

  document.getElementById("btn").addEventListener( "click" ,()=>{

      Promise.resolve().then( ()=>{
          console.log( count + "回目のclick" );
      }).finally(()=>{
          count ++;
      });

  });

});

要点はPromise.resolve()で、最初から解決済みのPromiseを利用する点です。
もしPromiseのcatchで補足させたいときは、Promise.reject()に置き換えてください。

 

繰り返し使用できる自作Promise

どうしても最初に失敗した方法でボタンクリックを処理したかったので、自分でコードを作成しました。

考え方は単純です。

thenやcatchの呼び出しを、配列に記憶しておきます。
コールバック関数でresolveまたはrejectが呼ばれたら、その都度Promiseを新規作成します。
そして作成したPromiseに対して、記憶しておいた順番でthenやcatchを実行するだけです。

const Promise2 = class{
  #currentPromise;
  #performs = [];
  constructor(executor){
    executor( this.#resolve.bind(this) , this.#reject.bind(this) );
  }
  // コールバックから呼び出されるメソッド
  #resolve( x ){this.#perform( "resolve" , x );}
  #reject( r ){this.#perform( "reject" , r );}

  // Promiseの生成
  #perform( type , v ){
    this.#currentPromise  = 
      type === "resolve" ? Promise.resolve(v) : Promise.reject(v);

    this.#performs.forEach( e=>this.#performApply(  e.method , e.args ) );
  };
  #performApply(m,args){
    this.#currentPromise = m.apply( this.#currentPromise,args );
  }

  // 
  #performSet( m , args ){
    this.#performs.push( {method:m , args:args} );
    if(this.#currentPromise) this.#performApply(  m , args );
  };
  then(...args){
    this.#performSet( Promise.prototype.then , args );
    return this;
  };
  catch(...args){
    this.#performSet( Promise.prototype.catch , args );
    return this;
  };
  finally(...args){
    this.#performSet( Promise.prototype.finally , args );
    return this;
  };
};

クリックなどの非同期処理を前提としていますが、constructorでのコールバック関数呼び出し時点で解決する可能性を考慮しています。

次のコードは、実行時に "0回目のclick" と出力されます。
そしてボタンをクリックする度に、"1回目のclick"、"2回目のclick"とカウントアップして出力されます。

document.addEventListener("DOMContentLoaded",()=>{
  let count = 0;
  new Promise2( (resolve)=>{
    resolve(); // コールバック関数呼び出し時点での解決

    document.getElementById("btn").addEventListener( "click" ,()=>resolve() );
  }).then( ()=>{
    console.log( count + "回目のclick" );
  }).finally(()=>{
    count ++;
  });
});

更新日:2024/01/18

書いた人(管理人):けーちゃん

スポンサーリンク

記事の内容について

null

こんにちはけーちゃんです。
説明するのって難しいですね。

「なんか言ってることおかしくない?」
たぶん、こんなご意見あると思います。

裏付けを取りながら記事を作成していますが、僕の勘違いだったり、そもそも情報源の内容が間違えていたりで、正確でないことが多いと思います。
そんなときは、ご意見もらえたら嬉しいです。

掲載コードについては事前に動作確認をしていますが、貼り付け後に体裁を整えるなどをした結果動作しないものになっていることがあります。
生暖かい視線でスルーするか、ご指摘ください。

ご意見、ご指摘はこちら。
https://note.affi-sapo-sv.com/info.php

 

このサイトは、リンクフリーです。大歓迎です。