こんにちは
この回答では3つのコードを挙げます。ご質問のタイトルに、
for文内にsetTimeoutがある関数に対して、・・・
とあったので、 まずご質問のコードにある forループを生かしたコードを回答します。その後、for文を使わないコードを2つ挙げます。
1. forループで、各文字を追加するPromiseの配列を作る。
各文字を追加していく処理をひとつのPromiseにして、それらがすべてresolve されたら、Doneを表示するという前後関係を担保するために、 Promise.all を使えばよいかと思います。
以下、ご質問に挙げられているコードに、上記の趣旨の追加をしたものです。
javascript
1const addTypingMovement = (word, target) => {
2 const promises = [];
3 const wordArray = [...word];
4 const printTarget = document.querySelector(target);
5
6 for(let i = 0; i < wordArray.length; i++) {
7 const p = new Promise(resolve => {
8 setTimeout(() => {
9 printTarget.textContent = printTarget.textContent + wordArray[i];
10 resolve();
11 }, i * 200)
12 });
13 promises.push(p);
14 }
15
16 return promises;
17}
18
19const finishTitleCall = () => {
20 document.querySelector('#result').textContent = 'Done.'
21}
22
23Promise.all(addTypingMovement('Hello World.', '#typing')).then(finishTitleCall);
以下は、ご質問に挙げられている jsFiddle をFork して、上記の修正版にしたものです。
2. forの替わりにmapを、前後関係の制御にasync/awaitを使用
上記 1. のコードで、Promiseの配列を作るところの for文を map を使って書き換え、 すべての文字が target
に追加されたら、Done を表示するために async
, await
を使った例が以下です。
javascript
1const addTypingMovement = (word, target) => {
2
3 const printTarget = document.querySelector(target);
4
5 return [...word].map((char, i) => new Promise(
6 resolve => {
7 setTimeout(() => {
8 printTarget.textContent += char;
9 resolve();
10 }, i * 200);
11 })
12 );
13
14}
15
16const finishTitleCall = () => {
17 document.querySelector('#result').textContent = 'Done.';
18}
19
20(async () => {
21 await Promise.all(addTypingMovement('Hello World.', '#typing'));
22 finishTitleCall();
23})();
24
3. 最後の文字まで表示するのを一つのPromiseにする。
上記の 1. と 2. とは異なり、「200ミリ秒間隔でひと文字ずつ、最後の文字まで表示する」という処理をひとつの Promise にすることもできます。以下のそのコード例です。
javascript
1const addTypingMovement = (word, target) => new Promise(
2 resolve => {
3 const printTarget = document.querySelector(target);
4 let i = 0;
5 const intervalId = setInterval(
6 () => {
7 if (i < word.length) {
8 printTarget.textContent = word.substring(0, ++ i);
9 } else {
10 clearInterval(intervalId);
11 resolve('Done.');
12 }
13 }, 200);
14 }
15);
16
17const finishTitleCall = (message) => {
18 document.querySelector('#result').textContent = message;
19}
20
21(async () => {
22 const message = await addTypingMovement('Hello World.', '#typing');
23 finishTitleCall(message);
24})();
上記では、
setTimeout
の替わりにsetInterval
を使っています。
- 文字列全体が表示されるまでをひとつの Promise にするので、
Promise.all
は使わなくなっています。
Done.
という文字列を resolve
の引数で返させるようにしました。
以上、参考になれば幸いです。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2019/10/12 20:41 編集
2019/10/13 04:57
2019/10/13 20:28
2019/10/13 23:24