2

我正在尝试制作一个 node express 应用程序,在该应用程序中,我从不同的 url 获取数据,并调用 node-fetch 以提取某些页面的正文和有关某些 url 端点的其他信息。然后我想渲染一个 html 表以通过一组信息显示这些数据。我在调用呈现信息时遇到了麻烦,因为所有函数都是异步的,因此在调用呈现页面之前很难确保所有的 promise 调用都已解决。我一直在研究使用 bluebird 和 .finally() 和 .all() 的其他承诺调用,但它们似乎不适用于我的数据,因为它不是一组承诺调用,而是一组对象。每个对象都是 4 个 promise 调用,用于在一行中获取与我的表的一列相关的数据。

var express = require('express');
var fetch = require('node-fetch');
fetch.Promise = require('bluebird');
var router = express.Router();
const client = require('../platform-support-tools');


function makeArray() {
    var registry = client.getDirectory();

    var data_arr = [];
    for (var i = 0; i < registry.length; i++) {
        var firstUp = 0;
        for (var j = 0; i < registry[i]; j++) {
            if (registry[i][j]['status'] == 'UP') {
                firstUp = j;
                break;
            }
        }
        var object = registry[i][firstUp];
        
        data_arr.push({
            'name': object['app'],
            'status': object['status'],
            'swagUrl': object['homePageUrl'] + 'swagger-ui.html',
            'swag': getSwag(object),
            'version': getVersion(object['statusPageUrl']),
            'timestamp': getTimestamp(object['statusPageUrl']),
            'description': getDescription(object['healthCheckUrl'])
        });
    }
    return data_arr;
}

function getSwag(object_in) {
    var homeUrl = object_in['homePageUrl'];
    if (homeUrl[homeUrl.length - 1] != '/'){
        homeUrl += '/';
    }
    var datum = fetch(homeUrl + 'swagger-ui.html')
        .then(function (res) {
            return res.ok;
        }).catch(function (err) {
            return 'none';
        });
    return datum;
}


function getVersion(url_in) {
    var version = fetch(url_in)
        .then(function(res) {
            return res.json();
        }).then(function(body) {
            return body['version'];
        }).catch(function (error) {
            return 'none';
        });
    return version;
}

function getTimestamp(url_in) {
    var timestamp = fetch(url_in)
        .then(function(res) {
            return res.json();
        }).then(function(body) {
            return body['timestamp'];
        }).then(function (res) {
            return body['version'];
        }).catch(function (error) {
            return 'none';
        });
    return timestamp;
}

function getDescription(url_in) {
    var des = fetch(url_in)
        .then(function(res) {
            return res.json();
        }).then(function(body) {
            return body['description'];
        }).catch(function (error) {
            return 'none';
        });
    return des;
}


/* GET home page. */
router.get('/', function (req, res, next) {
    var data_arr = makeArray();
    
    Promise.all(data_arr)
        .then(function (response) {
            //sorting by app name alphabetically
            response.sort(function (a, b) {
                return (a.name > b.name) ? 1 : ((b.name > a.name) ? -1 : 0);
            });
            res.render('registry', {title: 'Service Registry', arr: response})
        }).catch(function (err) {
        console.log('There was an error loading the page: '+err);
    });
});

4

1 回答 1

5

要等待所有这些承诺,您必须将它们放入一个数组中,以便您可以使用Promise.all()它们。你可以这样做:

let promises = [];
for (item of data_arr) {
    promises.push(item.swag);
    promises.push(item.version);
    promises.push(item.timestamp);
    promises.push(item.description);
}
Promise.all(promises).then(function(results) {
    // all promises done here
})

如果你想要所有这些承诺的价值,回到那个需要更多工作的对象。

let promises = [];
for (item of data_arr) {
    promises.push(item.swag);
    promises.push(item.version);
    promises.push(item.timestamp);
    promises.push(item.description);
}
Promise.all(promises).then(function(results) {
    // replace promises with their resolved values
    let index = 0;
    for (let i = 0; i < results.length; i += 4) {
         data_arr[index].swag = results[i];
         data_arr[index].version = results[i + 1];
         data_arr[index].timestamp = results[i + 2];
         data_arr[index].description = results[i + 3];
         ++index;
    });
    return data_arr;
}).then(function(data_arr) {
    // process results here in the array of objects
});

如果您必须更频繁地执行此操作,那么您可以删除属性名称的硬编码并可以迭代所有属性,收集包含承诺的属性名称并自动处理那些。


而且,这是一个更通用的版本,它采用对象数组,其中对象的某些属性是承诺。这个实现修改了对象的 promise 属性(它不复制对象的数组)。

function promiseAllProps(arrayOfObjects) {
    let datum = [];
    let promises = [];

    arrayOfObjects.forEach(function(obj, index) {
        Object.keys(obj).forEach(function(prop) {
            let val = obj[prop];
            // if it smells like a promise, lets track it
            if (val && val.then) {
                promises.push(val);
                // and keep track of where it came from
                datum.push({obj: obj, prop: prop});
            }
        });
    });

    return Promise.all(promises).then(function(results) {
        // now put all the results back in original arrayOfObjects in place of the promises
        // so now instead of promises, the actaul values are there
        results.forEach(function(val, index) {
            // get the info for this index
            let info = datum[index];
            // use that info to know which object and which property this value belongs to
            info.obj[info.prop] = val;
        });
        // make resolved value be our original (now modified) array of objects
        return arrayOfObjects;
    });
}

你会像这样使用它:

// data_arr is array of objects where some properties are promises
promiseAllProps(data_arr).then(function(r) {
    // r is a modified data_arr where all promises in the 
    // array of objects were replaced with their resolved values
}).catch(function(err) {
    // handle error
});

使用Bluebird Promise 库,你可以同时使用Promise.map()Promise.props(),上面的函数就是这样:

function promiseAllProps(arrayOfObjects) {
    return Promise.map(arrayOfObjects, function(obj) {
        return Promise.props(obj);
    });
}

Promise.props()迭代一个对象以查找所有具有 Promise 作为值的属性并用于Promise.all()等待所有这些 Promise,它返回一个具有所有原始属性的新对象,但 Promise 被解析的值替换。由于我们有一个对象数组,我们用它Promise.map()来迭代并等待整个数组。

于 2017-07-10T23:50:01.120 回答