从承诺中的callback中检索数据?

我现在有以下一段代码:

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,其数据被返回的是一个callback函数。

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); 

但是由于原因,只有前两个是按顺序执行的,而第三个是在读完之后随机执行的。 任何想法,我在这里做错了吗?

由于您使用的是蓝鸟,因此您可以使用.forEach()来replace.forEach()循环,并且它会按顺序遍历数组,等待每个asynchronous操作完成后再执行下一个。 结果将是一个承诺谁解决的价值是一系列的结果。 当涉及到asynchronous操作时,还应该停止在更高范围内声明局部variables。 在最近的范围内声明它们是实用的,在这种情况下是它们被使用的范围。

 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 }); 

变更摘要:

  1. 从recordPefMetrics返回承诺,以便调用者可以获取数据
  2. 使用Promise.mapSeries()而不是.forEach()进行顺序asynchronous操作。
  3. Promise.mapSeries()返回承诺,所以它与预先约定链接。
  4. 将variables声明移动到本地作用域中,以避免不同的asynchronous操作改变共享variables。
  5. logging之后,Rethrow .catch()错误,所以拒绝传播
  6. return perfData ,使其成为parsing值,并在结果数组中可用。