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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何使用Promise填充层次结构,其中每个级别都需要满足之前的级别?相关的知识,希望对你有一定的参考价值。

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

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永远不会阻止重复的Child数据库调用被触发,因为在promises完成解析之前不会填充该属性。

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

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

我已经在这方面工作了一段时间并且没有提出解决方案,所以任何可行的代码都不仅仅是值得赞赏的。

答案

问题中的代码很难遵循,但看起来你要做的事情相当简单,即串行执行一组异步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);
}

以上是关于如何使用Promise填充层次结构,其中每个级别都需要满足之前的级别?的主要内容,如果未能解决你的问题,请参考以下文章

处理规则层次结构/树的最佳方法

如何使用 Promise 填充对象?

在 tibble 中的嵌套级别之间移动:如何引用存储在嵌套层次结构的上层中的数据

在 BigQuery 中展平嵌套层次结构

LeetCode 116. 填充每个节点的下一个右侧节点指针

Hive - 将层次结构表展平为级别