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

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

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

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

同期

複数のディレクトリに存在するファイルを更新した場合に、すべてのファイルにも更新が行われる事、又は、同じ記憶領域に同時にアクセスして内容の整合性が失われてしまう事をを防ぐ制御などを同期と呼びます。

非同期処理

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

Q&A

解決済

1回答

742閲覧

JavaScriptでの再帰関数を含んだ時の挙動

退会済みユーザー

退会済みユーザー

総合スコア0

JavaScript

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

同期

複数のディレクトリに存在するファイルを更新した場合に、すべてのファイルにも更新が行われる事、又は、同じ記憶領域に同時にアクセスして内容の整合性が失われてしまう事をを防ぐ制御などを同期と呼びます。

非同期処理

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

0グッド

0クリップ

投稿2018/05/25 08:46

編集2018/05/25 10:02

###質問
JavaScriptで
・ディレクトリ内のファイルを列挙し、配列化する再帰関数xがあり、
・その配列をログに表示する関数yがあります
これを逐次的に処理させたいのでpromiseなどを自学していますが、再帰処理が含まれる場合どうすればよいかわかりません。

###わからないところ
1.どの関数に、どうやってpromiseを使えばよいかわからない
2.そもそもpromiseがあまり理解できない
3.学習用に再帰のない関数でpromiseを使って試した時はできた。だが再帰処理になるとわからなくなる

3に関して、なぜ再帰だとわからないのか、というと、学習時はreturn new Promiseという形で、promiseをリターンしていたので、再帰の場合どう扱われるか、途端にわからなくなります

###該当コード

HTML

1<div id="dropzone"> 2 <button class="btn btn-primary btn-block" id="cover" data-toggle="collapse" data-target="#collapseSample1"> 3 ここにファイルを 4 <br> 5 ドロップしてください 6 </button> 7</div>

CSS

1#dropzone { 2 text-align: center; 3 width: 1000px; 4 height: 70px; 5 margin: 10px; 6 padding: 10px; 7 border-radius: 10px; 8 } 9 10#cover{ 11 position: absolute; 12 margin: 200px 400px; 13 width: 1000px; 14 height: 70px; 15 }

このコードは、ブラウザからドラッグ&ドロップでディレクトリを取得した時に動くコード

JavaScript

1document.getElementById("dropzone") 2.addEventListener("dragover", function (event) { 3 event.preventDefault(); 4}, false); 5 6document.getElementById("dropzone") 7.addEventListener("drop", function (event) { 8 let items = event.dataTransfer.items; 9 let results = []; 10 event.preventDefault(); 11 for (let i = 0; i < items.length; i++) { 12 let item = items[i].webkitGetAsEntry(); 13 scanFiles(item, results); 14 } 15 setTimeout(() => viewLog(results), 500); 16}

再帰関数x、この処理をし、results(再帰関数xにはtmpObjectで値が渡っている)にしっかり値が入った後に、関数yを実行したい

JavaScript

1function scanFiles(entry, tmpObject) { 2 switch (true) { 3 case (entry.isDirectory) : 4 let entryReader = entry.createReader(); 5 entryReader.readEntries(function (entries) { 6 for (let i = 0, len = entries.length; i < len; i++) 7 scanFiles(entries[i], tmpObject); 8 }); 9 break; 10 case (entry.isFile) : 11 tmpObject.push({"fileName": entry.name, "fullPath": entry.fullPath}); 12 break; 13 default : 14 break; 15 } 16}

関数y

JavaScript

1function viewLog(results) { 2 console.log(results); 3}

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

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

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

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

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

mather

2018/05/25 09:41 編集

逐次処理とは手順通りに順番に処理していくことを意味します。並列で動作するスクリプトに「逐次処理」と書くのは変な用語の使い方ですよ。つまり「逐次的に処理させたいのでpromiseなどを自学しています」とありますがpromiseは要らないことになってしまいます。
退会済みユーザー

退会済みユーザー

2018/05/25 09:52

申し訳ございません。同期処理と言えばよいでしょうか?Promiseを使わず実行すると、再帰関数xが終わる前にすべての関数が終了してしまい、再帰関数xで作られる配列が扱えないので、どうにか扱えるようにしたいです。setTimeoutで擬似的に処理していますが、取得したディレクトリの大きさがわからないのでsetTimeoutを使うと正しい処理がされない時があるのです。
mather

2018/05/25 09:56

提示されたコードを見る限り、scanFilesの処理が非同期で実行されているように見えないので、なにか省略されたコードがありませんか?
退会済みユーザー

退会済みユーザー

2018/05/25 09:58

省略しているコードは、HTML側の表示と、一番上で提示したコードを実行する時の判定のみです。この判定はHTML側で指定した範囲にディレクトリがドラッグ&ドロップされた時に実行するように書いてあります。本来のコードに修正しておきます。
guest

回答1

0

ベストアンサー

1.どの関数に、どうやってpromiseを使えばよいかわからない

自分でPromiseのインスタンスを作るのは、Promiseを返さないような非同期な処理をPromiseに変換するときです。今の場合entryReader.readEntries(...)がそれです。

scanFilesが処理を終えたタイミングで何かをするには、それがPromiseを返す必要があります。そしてそのPromiseはスキャンが終了した時にresolveしなければいけません。

js

1 for (let i = 0, len = entries.length; i < len; i++) 2 scanFiles(entries[i], tmpObject); 3 });

この部分は複数回scanFilesが呼ばれ、それがすべてresolveするまで待つ必要があるので、Promise.allか何かを使うといいです。

async/awaitを使うと楽です。
async/awaitもpromiseのsyntactic sugarですので、まずpromiseを理解することをおすすめします。

JSFiddle Demo

js

1window.addEventListener("dragover", e => { 2 e.preventDefault() 3}) 4 5window.addEventListener("drop", async e => { 6 e.preventDefault() 7 const items = e.dataTransfer.items 8 const results = [] 9 for (const item of items) { 10 const entry = item.webkitGetAsEntry() 11 await scanFiles(entry, results) 12 } 13 console.log(results) 14}) 15 16async function scanFiles(entry, tmpObject) { 17 if (entry.isDirectory) { 18 const entryReader = entry.createReader() 19 const entries = await new Promise((resolve, reject) => { 20 entryReader.readEntries( 21 entries => resolve(entries), 22 err => reject(err) 23 ) 24 }) 25 await Promise.all(entries.map(entry => scanFiles(entry, tmpObject))) 26 } else if (entry.isFile) { 27 tmpObject.push({ 28 "fileName": entry.name, 29 "fullPath": entry.fullPath 30 }) 31 } 32}

投稿2018/05/25 12:25

編集2018/05/26 05:57
karamarimo

総合スコア2551

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

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

退会済みユーザー

退会済みユーザー

2018/05/25 15:49

動作は僕が求めてる通りに動いてくれたので、promiseとasync/awaitをしっかり理解でき次第ベストアンサーにさせていただきます 本当にありがとうございました
karamarimo

2018/05/25 16:26

わからない部分があれば質問してください(といってもasync/awaitを一からすべて説明できないですが)。
退会済みユーザー

退会済みユーザー

2018/05/26 01:19

何から何までありがとうございます scanFilesのif(isDirectory)の中の処理を追っていくのが難しいのですが、 entriesにPromiseをいれて、その後のPromise.allで、代入されたPromiseの本来の値を取得し、isFileの時else分岐に飛んで中身が作られる、ということであっていますでしょうか?
karamarimo

2018/05/26 05:04 編集

> entriesにPromiseをいれて いえ、await してるので、entries には promise の resolve した値が入ります。 > その後のPromise.allで、代入されたPromiseの本来の値を取得し いえ、entries は Array ですから map で各要素を scanFiles します。scanFiles は Promise を返すので、この map の返り値は Promise の Array になります。そして Promise.all に Promise の Array を渡すと、その返り値はまた Promise で、それはArray 内のすべての Promise が resolve したとき resolve します。この Promise を await することにより、すべての scanFiles が完了するのを待つということになります。
退会済みユーザー

退会済みユーザー

2018/05/26 05:29

promiseのresolveした値が、最終的に欲しい値で、promiseは、後々そこに最終的な値が入るよ、という約束が入っている という認識でよろしいでしょうか、、恥ずかしながら未だにpromiseを理解できてなくて、、
karamarimo

2018/05/26 05:56

> promiseのresolveした値が、最終的に欲しい値 そうです。今の場合 scanFiles が返す Promise の resolve した値は使ってないですが。 > promiseは、後々そこに最終的な値が入るよ、という約束が入っている いえ、Promise は resolve または reject によって完了したとみなされます。処理が失敗したことを表すのに reject が使われます。なので当然、そのような約束はありません。resolve するかもしれないし、reject するかもしれないし、永久に何も起こらないかもしれません。例えば new Promise(() => {}) とすれば永久に resolve も reject もしないです。 言われて気づきましたが entryReader.readEntries はエラーが発生しうるので reject をちゃんとしたほうがよかったですね。 https://developer.mozilla.org/en-US/docs/Web/API/FileSystemDirectoryReader/readEntries 回答を編集しておきます。
退会済みユーザー

退会済みユーザー

2018/05/26 06:01

ありがとうございます。最後までしっかり教えていただけたおかげで、理解が深まりました。 自分の実現したい事もできるようになりました。本当に助かりました。ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問