(Node.js和Promises)找不到解决承诺的问题

时间:2019-02-07 18:24:11

标签: node.js promise sequelize.js

我正在使用Promise处理一些继承的代码,但遇到一个重大问题。我有两个函数,都返回一个promise(或在findOne情况下为一系列promise),其中findAll调用findOne来遍历许多记录,以使用Sequelize从其他表中的那些记录中查询数据。我已尝试对代码进行布局,以便在返回父函数findAll之前,解析或拒绝传递给findOne的所有promise。

给定findAll中的许多匹配记录,我将每条记录传递给findOne,当findOne解析它时,它将调用另一个函数ConstructJson,该函数返回一个值而不是一个Promise。我看到的问题是,一旦所有记录都通过findOne并返回到findAll,则代码实际上多次返回到findOne函数的resolve(constructJson(_map,scrub))行。在测试用例中,应该给我返回四个不同的记录ID(传递给findOne以进行查询),findOne返回的是重复的ID或乱序的ID。我很少会以传递给findOne的顺序返回相同的四个顺序ID,尽管基于函数的流程看来应该是这种情况。为了确保findOne返回的承诺已完全完成,传递给它的关联ID(查询对象)已按顺序返回,因此我缺少异步性。我没看到什么?

const Sequelize = require('sequelize');

function findAll(dbConn, _map, queries, joins, primaryTable, primaryKey, whereClause, limit, offset, scrub) {
    return new Promise((resolve, reject) => {
        const sequelize = new Sequelize(dbConn.db, dbConn.username, dbConn.password, dbConn.options);
        offset = isNaN(offset) ? 0 : offset;
        whereClause = !!whereClause ? ` WHERE true${whereClause}` : ' WHERE true';
        sequelize.query(`SELECT count(${primaryKey}) FROM ${primaryTable}${whereClause}`, {type: sequelize.QueryTypes.SELECT})
        .then(result => { 
            totalRows = result[0]["count"];
        });
        whereClause = isNaN(limit) ? whereClause : whereClause + ` LIMIT ${limit} OFFSET ${offset}`;

        sequelize.query(`SELECT ${primaryKey} FROM ${primaryTable}${whereClause}`, {type: sequelize.QueryTypes.SELECT})
        .then(matchingRecords => {
            let promiseStack = [];
            matchingRecords.forEach(record => {
                const cloneOfMap = JSON.parse(JSON.stringify(_map));
                promiseStack.push(
                    findOne(dbConn, cloneOfMap, queries, joins, primaryTable, primaryKey, record.id, scrub)
                    .then(result => {
                        return result;
                    })
                    .catch(error => {
                        return {err: `error collecting data for id = ${record.id}\n${error}`};
                    })
                )
            });

            Promise.all(promiseStack).then(bundleEntries => {
                sequelize.close();
                let errors = bundleEntries.filter(r => {return !!r.err}).map(r => r.err);
                if (errors.length === 0) {
                    resolve(bundleEntries);
                } else {
                    reject("The following queries failed:\n" + errors.join('\n'));
                }
            });
        })
        .catch(error => {
            sequelize.close();
            reject(error);
        });
    });
}

function findOne(dbConn, _map, queries, joins, primaryTable, primaryKey, id, scrub) {
    return new Promise((resolve, reject) => {
        let promiseStack = [];
        let noMatchesReturned = true;

        const sequelize = new Sequelize(dbConn.db, dbConn.username, dbConn.password, dbConn.options);

        let queryString;
        queries.forEach(query => {
            if (!!query.sql && query.sql !== '') {
                switch (dbConn.options.dialect) {
                    case "postgres":
                        queryString = query.sql + ` WHERE ${primaryTable}.${primaryKey}='${id}' LIMIT 1`;
                    break;
                    case "mssql":
                        queryString = query.sql + ` WHERE ${primaryTable}.${primaryKey}='${id}'`;
                    break;
                }
            }
            else {

                let otherTable = '';
                let joinString = '';
                if (query.table !== primaryTable) {
          otherTable = ", " + primaryTable;

          joins.filter(j => {return j.fkTable === primaryTable && j.pkTable === query.table}).forEach(j => {
            joinString += ` AND ${j.fkTable}.${j.fkColumn} = ${j.pkTable}.${j.pkColumn}`;     
          });
        }

        switch (dbConn.options.dialect) {
          case "postgres":
            queryString = `SELECT ${query.table}.${query.column} FROM ${query.table}${otherTable} WHERE ${primaryTable}.${primaryKey}='${id}'${joinString} LIMIT 1`;
            break;
          case "mssql":
            queryString = `SELECT TOP 1 ${query.table}.${query.column} FROM ${query.table}${otherTable} WHERE ${primaryTable}.${primaryKey}='${id}'${joinString}`;
            break;
        }
      }

      // console.log(queryString);

      promiseStack.push(
        sequelize.query(queryString, {type: sequelize.QueryTypes.SELECT})
        .then(row => {
          let newData;
          if (row.length === 0) {
            newData = null;
          } else {
            noMatchesReturned = false;
            const x = row[0];
            const y = Object.keys(x)[0];
            newData = x[y];
          }
          return Object.assign(query, {data: newData});
        })
        .catch(error => {
          return {err: `Error executing query ${error.sql}\n\t${error}`};
        })
      );
    });

    Promise.all(promiseStack).then(rows => {
      // identify queries that returned an error
      sequelize.close();
      let errors = rows.filter(r => {return !!r.err}).map(r => r.err);
      if (errors.length === 0) {
        if (noMatchesReturned) {
          resolve([]);
        }
        else {
          // all queries completed without error
          rows.forEach(result => {
            _map.rows[result.id].data = result.data;
          });
          resolve(constructJson(_map, scrub));
        }
      }
      else {
        sequelize.close();
        reject("The following queries failed:\n" + errors.join('\n'));
      }
    }, error => {
      sequelize.close();
      reject(error);
    });
  });
}

0 个答案:

没有答案