teratail header banner
teratail header banner
質問するログイン新規登録

質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.30%
JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

非同期処理

非同期処理とは一部のコードを別々のスレッドで実行させる手法です。アプリケーションのパフォーマンスを向上させる目的でこの手法を用います。

Q&A

解決済

1回答

175閲覧

JavaScript async/await処理がiOS/iPadOSでのみ動作しない

numin

総合スコア41

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

非同期処理

非同期処理とは一部のコードを別々のスレッドで実行させる手法です。アプリケーションのパフォーマンスを向上させる目的でこの手法を用います。

0グッド

1クリップ

投稿2025/06/07 06:45

0

1

実現したいこと

JavaScriptの非同期処理を用いてHowler.jsによる音源再生とアラート表示を順番に実行させたい

前提

HTML/JavaScript/CSSを使ってブラウザ上で動作する音感・記憶力ゲームを作成しています。

その中でゲーム終了時に

  • 得点がハイスコアを上回っている場合にはlocalStorageを上書き
  • Howler.jsを通し、ハイスコア更新の有無に応じて音源(3秒程度)を再生
  • 3000ミリ秒(検証のため問題発生個所では倍に設定中)待機
  • ハイスコア更新の有無に応じてアラートを表示

という処理を設定しているのですが、何故かiOS/iPadOSでのみ、ハイスコアを更新できなかった場合の音源が再生されず、またゲーム終了後に再度スタートした場合にボタン押下時のビープ音が一切再生されなくなるという問題が発生しています。

なお、テスト環境・結果は以下の通りです。

OSブラウザ結果
Windows 11Chrome
Android 13Chrome
iOS/iPadOS 18Safari・chrome×

この問題の原因・解決策をご存じでしたら、教えていただけないでしょうか。

発生している問題・エラーメッセージ

発生している問題は下記の通りです。

  • ゲーム終了時(ハイスコア非達成)に限り設定した音源が再生されない
  • 2度目以降のゲーム開始時に出題・ボタン押下時のビープ音が再生されない

なおiPhone/iPadではコンソールを見ることができず、またWindows/Chrome環境では特にエラーは出力されていないようです。

該当のソースコード

JavaScript

1const notes = [261.63, 293.66, 329.63, 349.23, 392.00, 440.00, 493.88, 523.25]; // 各音階の周波数 2let sequence = []; 3let userInput = []; 4let score = 0; 5let isPlayerTurn = false; 6 7const context = new (window.AudioContext || window.webkitAudioContext)(); 8const buttons = document.querySelectorAll(".note-button"); 9const startBtn = document.getElementById("start-btn"); 10 11function sleep(ms) { 12 return new Promise(resolve => setTimeout(resolve, ms)); 13} 14 15function updateHighScore() { 16 let highscore = document.getElementById('highscore'); 17 let onkanHigh = 0; 18 if(localStorage.getItem('onkanHigh')) { 19 onkanHigh = localStorage.getItem('onkanHigh'); 20 } 21 highscore.innerText = `現在のハイスコア: ${onkanHigh}`; 22} 23 24function playTone(freq, duration = 500) { 25 const osc = context.createOscillator(); 26 const gain = context.createGain(); 27 28 osc.type = "sine"; 29 osc.frequency.value = freq; 30 31 osc.connect(gain); 32 gain.connect(context.destination); 33 gain.gain.setValueAtTime(0.2, context.currentTime); 34 35 osc.start(); 36 osc.stop(context.currentTime + duration / 1000); 37} 38 39function flashButton(noteIndex) { 40 const btn = document.querySelector(`.note-button[data-note="${noteIndex + 1}"]`); 41 btn.classList.add("active"); 42 setTimeout(() => btn.classList.remove("active"), 300); 43} 44 45function playSequence() { 46 isPlayerTurn = false; 47 let i = 0; 48 const interval = setInterval(() => { 49 const note = sequence[i]; 50 playTone(notes[note]); 51 flashButton(note); 52 i++; 53 if (i >= sequence.length) { 54 clearInterval(interval); 55 isPlayerTurn = true; 56 } 57 }, 700); 58} 59 60function nextLevel() { 61 const nextNote = Math.floor(Math.random() * 8); 62 sequence.push(nextNote); 63 userInput = []; 64 playSequence(); 65} 66 67async function endGame() { 68 isPlayerTurn = false; 69 let onkanHigh = parseInt(localStorage.getItem("onkanHigh")) || 0; 70 // ハイスコア更新時 71 if (score > onkanHigh) { 72 localStorage.setItem("onkanHigh", score); 73 playSuccess(); 74 await sleep(3000); 75 alert(`おめでとうございます、${score}回連続正解でハイスコア更新です!!`); 76 } else { 77 playFailure(); 78 await sleep(6000); 79 alert(`残念、ゲームオーバーです。今回の得点は${score}回でした。`); 80 } 81 score = 0; 82 sequence = []; 83 updateHighScore(); 84 document.getElementById('info').classList.remove('hidden'); 85 document.getElementById('buttons-container').classList.add('hidden'); 86} 87 88function handleUserInput(noteIndex) { 89 if (!isPlayerTurn) return; 90 91 playTone(notes[noteIndex]); 92 flashButton(noteIndex); 93 userInput.push(noteIndex); 94 95 for (let i = 0; i < userInput.length; i++) { 96 if (userInput[i] !== sequence[i]) { 97 endGame(); 98 return; 99 } 100 } 101 102 if (userInput.length === sequence.length) { 103 score++; 104 setTimeout(nextLevel, 1000); 105 } 106} 107 108buttons.forEach((btn) => { 109 btn.addEventListener("click", () => { 110 const noteIndex = parseInt(btn.dataset.note) - 1; 111 handleUserInput(noteIndex); 112 }); 113}); 114 115document.addEventListener("keydown", (e) => { 116 if (!isPlayerTurn) return; 117 const key = parseInt(e.key); 118 if (key >= 1 && key <= 8) { 119 handleUserInput(key - 1); 120 } 121}); 122 123startBtn.addEventListener("click", () => { 124 score = 0; 125 sequence = []; 126 document.getElementById('info').classList.add('hidden'); 127 document.getElementById('buttons-container').classList.remove('hidden'); 128 nextLevel(); 129}); 130 131document.addEventListener("DOMContentLoaded", updateHighScore);

※サーバ上で実際に動作するページはこちらからご確認ください。 (アフィリエイト・広告などは含まれていません。

試したこと

iOS/iPadOSでは処理により時間がかかるのかと思い、待機時間を2倍まで伸ばしましたが効果はありませんでした。


以上です。何卒よろしくお願いいたします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

numin

2025/06/07 07:53

コメント・ご助言ありがとうございます。 まずAudioContextの不安定性についてあまり存じていなかったのですが、初回プレイ時にビープ音再生に問題がないこと、そしてasyncファンクション内で発生していることからの予測でした。 基礎知識が足りておらずお手数をおかけしますが、よろしくお願いいたします。
guest

回答1

0

自己解決

1日色々な検証を重ね、なんとか解決することができました。

なおコメント欄でご指摘いただいた通り、非同期処理ではなくAudio Context関連の問題っだったようです。

以下、解決いできた手順を記載します。


1.playFailure()ファンクションが正しく動作しなかった件

MP3形式はiOS/iPadOS環境において問題が起きやすいということで、M4A形式に変換して利用しました。

これにより奇数回のプレイでは正しく音源が再生されるようになりました。

偶数回では何故か相変わらず再生されなかったものの、最終的にゲーム終了後、結果通知のalertをconfirmに変え、「OK」が選ばれた場合にページを再読み込みする形に落ち着きました。

2.2度目以降の開始時にビープ音が再生されなくなっていた件

まずは下記の通り、ゲーム開始時にAudio Contextをリロードさせることで解決できました。
ただ最終的には前述の通りゲーム再開時にページそのものをリロードする形に変更したため、どちらにしても問題なく動作するようになりました。

JavaScript

1startBtn.addEventListener("click", async () => { 2 if (context.state === 'suspended') { 3 await context.resume(); 4...

以上、状況を開設させていただきました。

閲覧・ご協力いただいた皆様、ありがとうございました。

投稿2025/06/08 03:18

numin

総合スコア41

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.30%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問