从承诺中的回调中检索数据?

时间:2016-10-25 01:21:45

标签: javascript node.js promise bluebird es6-promise

我现在有以下代码:

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);与存储在文件中的网址的顺序相同,

但是console.log("inside browserPerf", url);不是和预期的一样。

我希望网址的顺序为:

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

但是由于理由,前两个按顺序执行,但第三个在被读取后随机触发。 知道我在这里做错了吗?

1 个答案:

答案 0 :(得分: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
});

变更摘要:

  1. 从recordPefMetrics返回承诺,以便调用者可以获取数据
  2. 使用Promise.mapSeries()代替.forEach()进行顺序异步操作。
  3. Promise.mapSeries()返回承诺,因此它与之前的承诺相关联。
  4. 将变量声明移动到本地作用域,这样就不会改变对共享变量进行踩踏的不同异步操作。
  5. 记录后
  6. Rethrow .catch()错误,因此拒绝传播
  7. return perfData因此它成为已解析的值,并且在结果数组中可用。
相关问题