

monacaでandroidアプリを開発しています。

フォルダ内の複数ファイルの更新日付を取得するプログラムを作成していますが、  
callbackは非同期で動くためこれを1件ずつ確実に処理をするようにしたいのですが、  
callbackが完了するまで処理を待つことは可能でしょうか？

宜しくお願い致します。

[code]

    for (var index = 0; index < fileEntries.length; index++) {
      let entry = fileEntries[index];
      // とりあえず対象は.apkファイルのみ
      let name = entry.name.toLowerCase();
      if (name.indexOf('.apk') != -1){
        // この処理を完了まで待ちたい！！！
        entry.getMetadata(getMetadataSuccess, fail);
      }
    }
    
    function getMetadataSuccess(metadata) {
      //alert("modificationTime: "+  moment(String(metadata_datetime.toLocaleString())).format('YYYY/MM/DD HH:mm:ss'));
    }
    
[/code]


CC BY-SA 4.0
2020-08-05T23:52:54.597
69288
2023-03-05T19:26:23.317
2020-08-06T00:24:24.737
3060
41126
question
javascript monaca
callback関数が終了するまで処理を待ちたい。


entry.getMetadata
というものが非同期関数なので、そこの先頭にawaitをつければうまくいくかもしれません。その場合でも、下記の(B)の例のようにmain関数をasnyc関数として作成してforループを回す必要があるでしょう。

promise と async/await という仕組みをつかってサンプルを書いてみました。これで非同期処理を同期処理のようにもっていくことができます。  
戻り値にpromiseを返すコードはawaitで待機できるようになります。

このような非同期処理に関係するプログラミングは通常の同期処理のプログラミングよりも何段階か難しさが増えますので、しっかりとサンプル動かしながら学び多く書いてなれていくことをおすすめします。  
promiseや async/await のサンプルをいくつもみて学びながら理解してください。

私の方も動作確認しながら書いてみました。

(A)setTimeoutをつかって非同期処理をシミュレートしてばらばらに動くもの  
<https://jsbin.com/fedisameje/edit?js,console>

(B)setTimeoutを使った非同期処理を同期的に処理するためにpromiseとasync/awaitを使ったもの  
<https://jsbin.com/pekuvayezo/edit?js,console>

(A)

[code]

    const randomInt = (min, max) => {
      return Math.floor( Math.random() * (max + 1 - min) ) + min;
    };
    
    const entry_getMetadata = (i, name) => {
      const interval = randomInt(1, 10);
      setTimeout(() => {
        console.log(`entry_getMetadata name:${name} index:${i} interval:${interval}`)
      }, interval);
    };
    
    const fileEntries = [
      {name: 'test1.txt'},
      {name: 'test2.apk'},
      {name: 'test3.apk'},
      {name: 'test4.apk'},
      {name: 'test5.apk'},
      {name: 'test6.apk'},
      {name: 'test7.apk'},
    ];
    
    for (var index = 0; index < fileEntries.length; index++) {
      let entry = fileEntries[index];
      let name = entry.name.toLowerCase();
      if (name.indexOf('.apk') !== -1){
        entry_getMetadata(index, name);
      }
    }
    
    // console
    // "entry_getMetadata name:test5.apk index:4 interval:3"
    // "entry_getMetadata name:test6.apk index:5 interval:4"
    // "entry_getMetadata name:test3.apk index:2 interval:8"
    // "entry_getMetadata name:test4.apk index:3 interval:9"
    // "entry_getMetadata name:test2.apk index:1 interval:10"
    // "entry_getMetadata name:test7.apk index:6 interval:10"
    // intervalが乱数指定のため実行順番はばらばらになる
    
[/code]

(B)

[code]

    const randomInt = (min, max) => {
      return Math.floor( Math.random() * (max + 1 - min) ) + min;
    };
    
    const entry_getMetadata = (i, name) => {
      const interval = randomInt(1, 10);
      
      return new Promise((resolve) => { 
          setTimeout(() => { 
            console.log(`entry_getMetadata name:${name} index:${i} interval:${interval}`);
            resolve();
          }, interval)
        })
    };
    
    const fileEntries = [
      {name: 'test1.txt'},
      {name: 'test2.apk'},
      {name: 'test3.apk'},
      {name: 'test4.apk'},
      {name: 'test5.apk'},
      {name: 'test6.apk'},
      {name: 'test7.apk'},
    ];
    
    const main = async () => {
      for (var index = 0; index < fileEntries.length; index++) {
        let entry = fileEntries[index];
        let name = entry.name.toLowerCase();
        if (name.indexOf('.apk') !== -1){
          await entry_getMetadata(index, name);
        }
      }
    }
    
    main();
    
    // console
    // "entry_getMetadata name:test2.apk index:1 interval:7"
    // "entry_getMetadata name:test3.apk index:2 interval:9"
    // "entry_getMetadata name:test4.apk index:3 interval:5"
    // "entry_getMetadata name:test5.apk index:4 interval:10"
    // "entry_getMetadata name:test6.apk index:5 interval:1"
    // "entry_getMetadata name:test7.apk index:6 interval:1"
    // intervalが乱数指定であっても、処理を一つづつ待機するので、
    // 処理順は常に実行順になる
    
    
[/code]


CC BY-SA 4.0
2020-08-10T12:33:44.053
69424
2020-08-10T13:24:07.420
2020-08-10T13:24:07.420
21047
21047
69288
answer


[Promise](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise)
を作って、[Promise.allSettled()](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled)で一気に待つのことができます。

[code]

    let promises = [];
    
    for (var index = 0; index < fileEntries.length; index++) {
      let entry = fileEntries[index];
      // とりあえず対象は.apkファイルのみ
      let name = entry.name.toLowerCase();
      if (name.indexOf('.apk') != -1){
        // この処理を完了まで待ちたい！！！
        let promise = new Promise((resolve,reject)=>{
            entry.getMetadata(resolve, reject);
        });
        promises.push(promise);
      }
    }
    
    Promise.allSettled(promises).then(metadataArray => {
        //ここで続く。metadataArrayはmetadataの配列です
    });
    
[/code]


CC BY-SA 4.0
2021-12-28T09:23:02.863
85374
2021-12-28T09:35:44.827
2021-12-28T09:35:44.827
40660
40660
69288
answer


僕はPromiseをこう言うふうに使っています。

[code]

    /* 即時関数 */
    (async() => {
      async function get() {
        return await new Promise(resolve => getMetadata(meta => resolve(meta), fail))
      }
      const metadata = await get() //ここでデータ取得を待機し取得
    })()
    
[/code]

そして、それを適用するとこうなります。

[code]

    async function get(entry) {
      return await new Promise(resolve => entry.getMetadata(meta => resolve(meta), fail))
    }
    (async() => {
      for (var index = 0; index < fileEntries.length; index++) {
        let entry = fileEntries[index];
        // とりあえず対象は.apkファイルのみ
        let name = entry.name.toLowerCase();
        if (name.indexOf('.apk') != -1){
          getMetadataSuccess(await get(entry))
        }
      }
    })()
    
    function getMetadataSuccess(metadata) {
      //alert("modificationTime: "+  moment(String(metadata_datetime.toLocaleString())).format('YYYY/MM/DD HH:mm:ss'));
    }
    
[/code]

なんとなくPromiseをおさらいします。  
`new Promise()`は何となく分かればいいです。  
`await`は`async`が付いた関数のみで動きます。  
`resolve => {}`は分かりやすくすれば`function(resolve) {}`が入っているようなものです。  
`resolve`にデータを返すと処理が完了したことになります。  
データはこう取得します。

[code]

    const data = await new Promise(resolve => resolve("文字列データ")) //async内で動く
    new Promise(resolve => resolve("文字列データ2")) //あたかもcallbackかのように動く
      .then(data2 => {
        console.log([data, data2]) //["文字列データ", "文字列データ2"]
      })
    
[/code]

僕はプログラムの内容を見ていません。  
質問文に返答しただけなので、役に立たない可能性があります。  
ですが、ちょっと内容読んだ所、failをfileと読み間違えていたので、修正しておきます。が、failの宣言が僕には無いように思えるので、その辺はお任せします。


CC BY-SA 4.0
2023-03-04T14:26:35.260
94069
2023-03-05T19:26:23.317
2023-03-05T19:26:23.317
52710
52710
69288
answer