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

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

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

Firebaseは、Googleが提供するBasSサービスの一つ。リアルタイム通知可能、並びにアクセス制御ができるオブジェクトデータベース機能を備えます。さらに認証機能、アプリケーションのログ解析機能などの利用も可能です。

JavaScript

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

非同期処理

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

Q&A

解決済

4回答

637閲覧

【Firebase】非同期処理の中にpromiseを記述したい

olee46

総合スコア32

Firebase

Firebaseは、Googleが提供するBasSサービスの一つ。リアルタイム通知可能、並びにアクセス制御ができるオブジェクトデータベース機能を備えます。さらに認証機能、アプリケーションのログ解析機能などの利用も可能です。

JavaScript

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

非同期処理

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

0グッド

0クリップ

投稿2018/09/16 05:24

####やりたいこと
非同期処理のコールバックの中で、別の非同期処理の結果を利用したい

####困っていること
コールバックの中の非同期処理が終わらないまま、もとの非同期処理に戻ってしまい、結果の表示が意図したものと異なる

####コード

javascript

1// DB listener 2boardRef.orderByKey().on('child_added', (data) => { 3 console.log(data.key); 4 var msg = data.child('message').val(); 5 var name = data.child('name').val(); 6 var ts = data.child('timestamp').val(); 7 var uid = data.child('uid').val(); 8 9 // replace newlines with brs 10 msg = msg.replace(/\n/g, '<br>'); 11 12 // get photo 13 var ref = firebase.storage().ref('user-photo/' + uid); 14 ref.getDownloadURL().then(url => { 15 $('table').prepend('<tr><td><img src="' + url + '" class="user-pic"></td><td><b>' + 16 name + ' </b> ' + ts + '<br>' + msg + '</td></tr>'); 17 18 }).catch(error => { 19 $('table').prepend('<tr><td><img src="/images/monster.png" class="user-pic"></td><td><b>' + 20 name + ' </b> ' + ts + '<br>' + msg + '</td></tr>'); 21 }); 22 23})

child_addedイベントは、データベースにデータが追加されるたびに呼ばれますが、consoleに出力しているデータのキーの順番(コード内のconsole.log(data.key))と、実際にページに表示される順番が異なっています。
この原因として、child_addedイベントの処理内にref.getDownloadURL()という非同期処理が入っていることがあると思うのですが、child_addedイベントはPromiseではないので、.then()でつなげることができず、解決策がわかりません。

よろしくお願いします。

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

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

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

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

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

guest

回答4

0

多数の回答を失礼いたします…。

ベストアンサーに選んでいただいたコードですが、よく見たらref.getDownloadURL()が同時に1つしか実行されないという問題がありました。
パフォーマンスを考慮するのであれば、下記のコードのようにPromise.all()を使って「『ref.getDownloadURL()』と『直前の処理』の両方が終わったらDOMを追加する」ようなコードにすればref.getDownloadURL()を並列実行できるので、実行にかかる時間が短縮されると思います。

Promise.all()は引数に与えたPromiseの配列が1つでもrejectされると戻り値のPromiseが即座にrejectされてしまうため、最後に.catch(() => {})を付け加えてprevTaskがrejectされないようにしています。

javascript

1var prevTask = Promise.resolve(); 2 3boardRef.orderByKey().on('child_added', (data) => { 4 console.log(data.key); 5 // 中略 6 prevTask = Promise.all([prevTask, ref.getDownloadURL()]).then(([_, url]) => { 7 // ... 8 }).catch(error => { 9 // ... 10 // ↓ この`.catch()`内で発生した例外を無視してPromiseをrejectからresolveにする 11 }).catch(() => {}); 12});

投稿2018/09/16 11:28

reosablo

総合スコア339

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

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

0

(先の回答とは別のアプローチなので回答を分けます。)
質問の趣旨とは異なるのですが、先にimg要素を追加しておいて、後からsrc属性をすり替えるのもありかと思います。

javascript

1// get photo 2var tr = $('<tr><td><img src="/images/loading-animation.gif" class="user-pic"></td><td><b>' + 3 name + ' </b> ' + ts + '<br>' + msg + '</td></tr>'); 4$('table').prepend(tr); 5 6var ref = firebase.storage().ref('user-photo/' + uid); 7ref.getDownloadURL().then(url => { 8 tr.find('img').prop('src', url); 9}).catch(error => { 10 tr.find('img').prop('src', '/images/monster.png'); 11});

投稿2018/09/16 08:08

reosablo

総合スコア339

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

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

0

ベストアンサー

結果が意図したものと異なる原因は「コールバック中の非同期処理が終わらないまま、次のコールバックが始まってしまう」からです。
つまり、直前の非同期処理が終わってからコールバックが始まるようにすればよいかと思います。

コード中に現れるasyncというのは非同期関数の文法で、実行するとPromiseを生成することができます。生成されたPromiseは関数がreturnもしくは処理が最後まで到達するとresolveされます。
asyncキーワード付きの関数内ではawait演算子を使用することができ、演算子の対象に指定したPromiseのresolve/rejectまで関数の処理を一時停止することができます。

javascript

1var prevTask = Promise.resolve(); 2 3boardRef.orderByKey().on('child_added', (data) => { 4 prevTask = prevTask.finally(async () => { 5 console.log(data.key); 6 // 中略 7 // ↓`await`を追加して非同期処理の完了を待つ 8 await ref.getDownloadURL().then(url => { 9 // ... 10 }).catch(error => { 11 // ... 12 }); 13 }); 14});

投稿2018/09/16 08:07

編集2018/09/16 08:54
reosablo

総合スコア339

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

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

reosablo

2018/09/16 08:09

回答内容が盛大に間違っていたので修正します…。
reosablo

2018/09/16 08:39

修正しました。編集前のコードでは`prevTask`をresolve/rejectさせるのを忘れていました。
olee46

2018/09/16 09:07

ありがとうございます!ご説明いただいた方法で解決しました。別のアプローチも示していただき勉強になりました。 後学のために、もしよろしければ教えていただきたいのですが、このasync/awaitや、Promise.resolve()を使うやり方が自分では全く思いつきませんでした。このようなやり方を自分でも考えられるようにするためには、何か良い本やリソースはありますでしょうか?ご返信いただければ幸いです。
reosablo

2018/09/16 10:43

勉強になったと言ってもらえるのはとても嬉しい限りです。 参考になるかわかりませんが、非同期処理だけど順番付けたいというのはよくあるシチュエーションなので、どこかで見たことがあるようなコードを今回の場合に当てはめた形でコードを書きました。(英語や数学の習得と似たような感じだと思います。) 私の場合、このコードは たくさんのPromiseを一直線に並べてつなげて連鎖的に爆発させていく というようなイメージを持っています。 本やリソースについて、申し訳ありませんが体系的に学んだものが無いので紹介できるほどのおすすめのものはありません。 Web開発の技術は進化が速いので、新情報にアンテナ張っておいて、ピンとくるものがあったら簡単な使い方とか概念だけ心に留めておいて、使えそうな場面が現れたら実践してみる…といったような形で身に付けています。 完全に独学なのでこれがベストなのかわかりませんが、自分には合ったやり方だと思っています。 コードを書く機会がある限り、きっと進化し続けられることと思います。 質問者様に合った勉強法が見つかりますよう…!
guest

0

これでうまくいったりしません?

js

1boardRef.orderByKey().on('child_added', async (data) => { 2 console.log(data.key); 3 var msg = data.child('message').val(); 4 var name = data.child('name').val(); 5 var ts = data.child('timestamp').val(); 6 var uid = data.child('uid').val(); 7 8 // replace newlines with brs 9 msg = msg.replace(/\n/g, '<br>'); 10 11 // get photo 12 var ref = firebase.storage().ref('user-photo/' + uid); 13 14 try { 15 const url = await ref.getDownloadURL(); 16 $('table').prepend('<tr><td><img src="' + url + '" class="user-pic"></td><td><b>' + name + ' </b> ' + ts + '<br>' + msg + '</td></tr>'; 17 } catch (error) { 18 $('table').prepend('<tr><td><img src="/images/monster.png" class="user-pic"></td><td><b>' + name + ' </b> ' + ts + '<br>' + msg + '</td></tr>'); 19 } 20});

投稿2018/09/16 07:47

spookybird

総合スコア1803

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問