如何使用Promise填充层次结构,其中每个级别都需要满足之前的级别?

时间:2018-03-14 21:00:03

标签: javascript node.js promise sequelize.js

这是我尝试过的第一个代码版本。我已经尝试了很多其他的东西,比如互斥,在任何地方添加catch块,以及Promise反模式,但我似乎无法克服这种心理或语法障碍:

cost_calc.style.format(formatdict)

这是populateJoins() { let promises = []; for (let c in this.columns) { let transformColumn = this.columns[c]; if (transformColumn.joins) { let joinPointer = this.databaseObject; for (let j in transformColumn.joins) { let join = transformColumn.joins[j]; if (joinPointer[join.as] != null) { joinPointer = joinPointer.dataValues[join.as]; } else { if (this.requestQuery[toCamelCase(join.as) + 'Id']) { promises.push( join.model.findOne({where: {id: this.requestQuery[toCamelCase(join.as) + 'Id']}}) .then((tmp) => { joinPointer.dataValues[join.as] = tmp; })); } else if (joinPointer[toSnakeCase(join.as) + '_id']) { promises.push( join.model.findOne({where: {id: joinPointer[toSnakeCase(join.as) + '_id']}}) .then((tmp) => { joinPointer.dataValues[join.as] = tmp; })); } } } } } return Promises.all(promises); }

的结构
this.columns

因此,对于我已经学过的东西, child1Name: { name: 'name', forceSelect: true, joins: [{ model: Database.getInstance().getModel('child1'), as: 'Child1' }], hidden: true }, child2Name: { name: 'name', forceSelect: true, joins: [{ model: Database.getInstance().getModel('child2'), as: 'Child2' }], hidden: true }, child1Status1Name: { name: 'name', forceSelect: true, joins: [{ model: Database.getInstance().getModel('child1'), as: 'Child1' },{ model: Database.getInstance().getModel('status1'), as: 'Status1' }], hidden: true }, child1Status2Name: { name: 'name', forceSelect: true, joins: [{ model: Database.getInstance().getModel('child1'), as: 'Child1' },{ model: Database.getInstance().getModel('status2'), as: 'Grandchild2' }], hidden: true }, serverName: { name: 'name', forceSelect: true, joins: [{ model: Database.getInstance().getModel('child1'), as: 'Child2' },{ model: Database.getInstance().getModel('grandchild'), as: 'Grandchild' },{ model: Database.getInstance().getModel('great_grandchild'), as: 'GreatGrandchild' }], hidden: true }, child2Status1Name: { name: 'name', forceSelect: true, joins: [{ model: Database.getInstance().getModel('child2'), as: 'Child2' },{ model: Database.getInstance().getModel('status1'), as: 'Grandchild1' }], hidden: true }, child2Status2Name: { name: 'name', forceSelect: true, joins: [{ model: Database.getInstance().getModel('child2'), as: 'Child2' },{ model: Database.getInstance().getModel('status2'), as: 'Grandchild2' }], hidden: true }, archetypeName: { name: 'name', forceSelect: true, joins: [{ model: Database.getInstance().getModel('child2'), as: 'Child2' },{ model: Database.getInstance().getModel('archetype'), as: 'Archetype' },{ model: Database.getInstance().getModel('archetype'), as: 'ArchetypeLink' }], hidden: true }, 将永远不会阻止重复的joinPointer[join.as] != null数据库调用被触发,因为在promises完成解析之前不会填充该属性。

同样地,没有孙子会填充,因为他们必须等待孩子们居住,如果孙子们先实现,那么他们将永远不会进入子对象。对曾孙子来说同样如此。

我读了this answer,他说,“如果你已经将它们放在一个数组中,那么它们就已经在执行了。”我知道Promise的内容已经解决,这就是为什么在其他代码中我总是使用数字索引来填充对象,即Child,但我不知道如何在这里做到这一点。

我一直在研究这个问题并且没有提出解决方案,所以任何可行的代码都不仅仅是值得赞赏的。

1 个答案:

答案 0 :(得分:1)

问题中的代码难以理解,但看起来您尝试做的事情相当简单,即执行一系列异步findOne查询串联并逐步构建一个更深层次的层次结构,包括返回的结果。

如果是,那么:

  • 您可以使用reduce()模式,请参阅“The Collection Kerfuffle”here
  • 完整的.columns对象是不必要的 - 您只需要.columns.greatGrandchildName.joins

代码应至少看起来像这样:

populateJoin(joins) {
    return joins.reduce((p, join) => {
        return p.then(obj => {
            let id = this.requestQuery[toCamelCase(join.as) + 'Id'] || obj[toSnakeCase(join.as) + '_id'] || obj[toSnakeCase(join.as + 's') + '_id'] || null;
            if(id) {
                return join.model.findOne({'where': { 'id': id }}).then(tmp => {
                    if (obj.dataValues[join.as]) {
                        return obj.dataValues[join.as];
                    } else {
                        obj.dataValues[join.as] = tmp;
                        return tmp; // 'tmp' will be `obj` at next iteration of the reduction.
                    }
                    return tmp; // 'tmp' will be `obj` at next iteration of the reduction.
                });
            } else {
                return obj; // send unmodified obj to next iteration of the reduction.
            }
        });
    }, Promise.resolve(this.databaseObject)) // starter promise for the reduction
    .then(() => this.databaseObject); // useful though not essential to make the top level object available to the caller's .then() callback.
}

populateJoins() {
    var promises = [];
    for (let c in this.columns) {
        let transformColumn = this.columns[c];
        if (transformColumn.joins) {
            promises.push(this.populateJoin(transformColumn.joins));
        }
    }
    return Promise.all(promises);
}