2016-10-25 6 views
1

私は今、次のコードを持っている:約束しているコールバックからデータを取得しますか?

const Promise = require('bluebird'); 
const readFile = Promise.promisify(fs.readFile); 
recordPerfMetrics: function(url) { 

    var self = this; 
    var perf, loadTime, domInteractive, firstPaint; 
    var perfData = {};  
    readFile('urls.txt', 'UTF-8').then(function (urls, err) { 
     if (err) { 
      return console.log(err); 
     } 

     var urls = urls.split("\n"); 
     urls.shift(); 

     urls.forEach(function(url) {  
      console.log(url); 
      self.getStats(url).then(function(data) { 
       data = data[0]; 
       loadTime = (data.loadEventEnd - data.navigationStart)/1000 + ' sec'; 
       firstPaint = data.firstPaint; 
       domInteractive = (data.domInteractive - data.navigationStart)/1000 + ' sec'; 

       perfData = { 
        'URL' : url, 
        'firstPaint' : firstPaint, 
        'loadTime' : loadTime, 
        'domInteractive' : domInteractive 
       }; 
       console.log(perfData); 
      }).catch(function(error) { 
       console.log(error); 
      }); 
     });  

     // console.log(colors.magenta("Starting to record performance metrics for " + url)); 
     // this.storePerfMetrics();      
    });  
}, 

getStats: function(url) { 
    return new Promise(function(resolve, reject){ 
     console.log("Getting data for url: ",url); 
     browserPerf(url, function(error, data) { 
      console.log("inside browserPerf", url); 
      if (!error) { 
       resolve(data); 
       } else { 
       reject(error); 
      } 
     }, { 
      selenium: 'http://localhost:4444/wd/hub', 
      browsers: ['chrome'] 
     }); 
    }); 
}, 

これは基本的にファイルからURLを読み込み、その後、そのデータが返されるコールバック関数である関数browserPerfを呼び出しています。

console.log("Getting data for url: ",url);は、ファイルに格納されたURLと同じ順序である

しかしconsole.log("inside browserPerf", url);は同じとして、予想されるような組み合わせではありません。私は、URLの順序があることを期待

console.log(url); 
console.log("Getting data for url: ",url); 
console.log("inside browserPerf", url); 

しかし、最初の2つだけの理由では、順番に実行されているが、すべてが読まれた後に三つがランダムに発射されます。 私はここで間違って何を考えている?

+1

あなたの '.forEach()'ループは同期して実行されます。内部の非同期操作はすべて一度に開始され、 '.forEach()'ループを無視して終了します。複数の約束が完了したときに調整したい場合は、それらを順番に実行するようにシーケンスするか、 'Promise.all()'を使用してすべての完了が通知されるようにします。これらのオプションのいずれかを実行する方法を説明する何百もの質問/回答があります。 – jfriend00

+0

また、Bluebirdを使用しているので、 '.forEach()'をBluebirdのマップ関数に置き換えてみてください。並行処理オプションも良いです。 – Keith

+0

URLを順番に並べ替えるか、すべての情報を並行して取得して、いつ準備が整ったのかを知りたいですか? – jfriend00

答えて

2

Bluebirdを使用しているため、.forEach()ループをPromise.mapSeries()に置き換えることができます。次の処理を実行する前に、各非同期処理が完了するのを待って配列を順番に処理します。結果は解決された約束になります。値は一連の結果です。また、非同期操作が含まれている場合は、より高いスコープでローカル変数の宣言を停止する必要があります。実際の最も近い範囲でそれらを宣言します。この場合は、使用される範囲です。

const Promise = require('bluebird'); 
const readFile = Promise.promisify(fs.readFile); 

recordPerfMetrics: function() { 

    var self = this; 
    return readFile('urls.txt', 'UTF-8').then(function (urls) { 
     var urls = urls.split("\n"); 
     urls.shift(); 

     return Promise.mapSeries(urls, function(url) {  
      console.log(url); 
      return self.getStats(url).then(function(data) { 
       data = data[0]; 
       let loadTime = (data.loadEventEnd - data.navigationStart)/1000 + ' sec'; 
       let firstPaint = data.firstPaint; 
       let domInteractive = (data.domInteractive - data.navigationStart)/1000 + ' sec'; 

       let perfData = { 
        'URL' : url, 
        'firstPaint' : firstPaint, 
        'loadTime' : loadTime, 
        'domInteractive' : domInteractive 
       }; 
       console.log(perfData); 
       return perfData; 
      }).catch(function(error) { 
       console.log(error); 
       throw error; // keep the promise rejected 
      }); 
     });  

     // console.log(colors.magenta("Starting to record performance metrics for " + url)); 
     // this.storePerfMetrics();      
    });  
}, 

getStats: function(url) { 
    return new Promise(function(resolve, reject){ 
     console.log("Getting data for url: ",url); 
     browserPerf(url, function(error, data) { 
      console.log("inside browserPerf", url); 
      if (!error) { 
       resolve(data); 
       } else { 
       reject(error); 
      } 
     }, { 
      selenium: 'http://localhost:4444/wd/hub', 
      browsers: ['chrome'] 
     }); 
    }); 
}, 

あなたはこのように、これを使用します。変更の

obj.recordPerfMetrics().then(function(results) { 
    // process results array here (array of perfData objects) 
}).catch(function(err) { 
    // error here 
}); 

概要:recordPefMetricsから

  1. リターンの約束を呼び出し側がデータのシーケンシャル用
  2. 利用Promise.mapSeries()の代わり.forEach()を得ることができるので、非同期操作。
  3. Promise.mapSeries()からの約束を返すので、それは前の約束で連鎖されます。
  4. 変数宣言をローカルスコープに移動するので、共有変数に異なる非同期操作が変更されることはありません。
  5. Rethrowログの後にエラーが発生するので、拒否は
  6. return perfDataに伝播するので、解決された値になり、結果配列で使用できるようになります。
+0

私が必要としていたものを正確に叩いてください!どうもありがとうございます!! – pj013

+0

@ jp310 - もう少し修正するためにやった編集を見逃してはいけません。 – jfriend00

+0

ちょうどそれらを見た、ありがとう! – pj013

関連する問題