時間を操る
スクリプト
javascriptのasyncとawaitを使って
準備が完了するまで
処理を待たせるスクリプト

image: 時間を操るスクリプト
Toplabo
Script to Manipulate Time

必要なDOMの構築が完了するまで待ってから、処理を実行するjavascript。

 | 2023/12/31

考え方

非同期JavaScriptについて考える
  • 歴史的に JavaScript は非同期関数で実装されてきた

    基本的にJavaScriptは関数を上から順番に処理していきます。これを同期処理といいます。しかし、処理が長く続く場合、ユーザーが何らかのアクションを起こしても無反応という状態が発生し、ユーザーにとっては止まっている(不具合や無反応)と認識されます。これを防ぐのが非同期プログラミングです。
    非同期プログラミングは、長く続く可能性のあるタスクを開始しても、そのタスクが完了するまで待つのではなく、そのタスクの実行中も他のイベントに応答できるようにする技術です。これがあるため、WEBサイトが表示されたとき、すぐにユーザーアクションをブラウザで処理することが可能になります。
    しかし、これの弊害としてDOMが構築されていないのに(データがまだ処理途中)、そのDOMへのイベント処理が始まってしまい、結果的に何もイベント処理が付加されていないDOMだけが表示されてしまうことがあります。
    これを防ぐために非同期関数を任意の順番で同期処理するための関数が実装されました。

    非同期 JavaScript 入門 | MDN:https://developer.mozilla.org/ja/docs/Learn/JavaScript/Asynchronous/Introducing

    非同期関数を任意の順番で同期処理するための関数

    非同期処理をさらに同期処理するというと元にもどったと思うかもしれませんが、正しくは非同期処理を任意の順番で同期処理させるということになります。この処理を可能にするのがasync,await関数です。
    次でasync,awaitを使わない場合と使う場合のサンプルを見てみます。

実装

データを読み込みDOMを構築し、ユーザーイベントを付与するサンプル
  • サンプルの基本構造

    二つの関数を同期処理で実装します。最初の関数でDOMを構築し、二つ目の関数で構築されたDOMへユーザーイベントを設定します。
    ユーザーイベントはマウスを要素へのせると画像が表示されるシンプルなものです。
    最初の関数の方に非同期関数のsetTimeoutを使い疑似的に非常に重たい処理の環境を作り出します。setTimeoutは非同期関数なので遅らせる時間はいくらでも1000分の1でもOKです。

    サンプル1

    最初にasync,awaitを使わないサンプルです。一番目のDOM構築が完了する前に二番目の関数が起動するため、マウスをのせても何も動きが発生しません。
    demo:https://labo.studio-happyvalley.com/async_function/noAsync.php

    サンプル2

    次にasync,awaitを使って一番目のDOM構築が完了するまでまってから二番目の処理が起動するようにします。
    demo2:https://labo.studio-happyvalley.com/async_function/index.php

コード

コードサンプルです
  • awaitを実装したサンプル2のコードです

    ファイル構造は下記の様になっています。
    index.php
    init.js
    modules/
    setAction.js

    /* init.js */
    import {setAction} from "./modules/setAction.js";
    import "../css/layout.css";
    	const i={
    		maxCount: 10,
    		data: 'item'
    	};
    	let n = 0;
    	const item= document.querySelector('#data');
    	const position= 'beforeend';	
    	function setready(){
    		return new Promise(resolve => {
    			setTimeout(() => {
    				while (n < i.maxCount) {
    					n++;
    					const html= `<div class="item" data-img="../../assets/labo/async_function/images/${i.data}_${n}.jpg"><span>${i.data}_${n}</span></div>`;
    					item.insertAdjacentHTML(position, html);
    				}
    				resolve();
    			}, 100);
    		});
    	}	
    	async function getready(){
    		const result = await setready();
    		setAction(result);
    	}	
    	getready();
    
    
    /* modules/setAction.js */
    
    function setAction(){
    	const items= document.querySelectorAll('#data div');
    	items.forEach(function(item, num){
    		item.addEventListener('mouseover', onActive, { passive: true });	
    		item.addEventListener('mouseout', onDeActive, { passive: true });	
    	});
    }
    
    function onActive(e){
    	e.preventDefault();
    	const position= 'beforeend';
    	const imgdata= e.currentTarget.dataset.img;
    	e.currentTarget.insertAdjacentHTML(position, `<img src="${imgdata}">`);
    }
    
    function onDeActive(e){
    	e.preventDefault();
    	e.currentTarget.querySelector('img').remove();
    }
    
    export { setAction };
    

『時間を操るスクリプト』関連のお薦め

このページで紹介しているプログラムやビジュアルなどご依頼いただければ実装を賜ります。
お問い合わせはこちら