Javascript 函数使用了 promises 并且没有返回正确的值

Javascript function using promises and not returning the correct value

作为 Javascript 开发的相对开端,我试图理解我在构建的函数中遇到的这个问题,该函数将成为连接到 postgreSQL 数据库的代码的一部分.

在此函数中,我使用 knex 查询生成器方法检查远程数据库中是否存在 table,并且此方法解析为布尔值,指示您指定的字符串是否与table 数据库中的同名。我提供了一个 knex 语法示例,以便人们更好地理解该功能。

knex.schema.hasTable('users').then(function(exists) {
  if (!exists) {
    return knex.schema.createTable('users', function(t) {
      t.increments('id').primary();
      t.string('first_name', 100);
      t.string('last_name', 100);
      t.text('bio');
    });
  }
});

我试图通过使用 .every Array 方法使我的函数 return 成为布尔值,该方法检查每个数组并且每个索引都应该通过定义的条件,.every 方法应该 return一个真值,否则为假。我构建了一个函数,该函数采用模式键数组或 table 的名称,并将其传递给 .every 方法。 .every 方法然后使用 knex.schema.hasTable 方法来 return 真或假。

我担心的是,通过函数的许多不同配置,我无法将其设置为 return 正确的值。不仅return取值不正确,可能跟.every有关系,我相信可以return "truthey"取值,而且定义函数后,经常会得到一个"Function undefined" 稍后在文件中调用它时出错。这是我的函数示例 - 我再次认为我对 returns、promises 和闭包如何协同工作的理解很差,但如果有人有见识,将不胜感激。

var schemaTables = ['posts','users', 'misc'];

// should return Boolean
function checkTable() {
    schemaTables.every(function(key) {
    return dbInstance.schema.hasTable(key)
        .then(function(exists) {
            return exists;
            });
    });
}

console.log(checkTable(), 'checkTable function outside');
// console.log is returning undefined here, although in other situations,
   I've seen it return true or false incorrectly.

您的功能无法正常工作有两个原因:

  1. 您没有在 checkTable 函数声明中 returning,因此它将始终 return 未定义。 你应该写:
function checkTable() {
  return schemaTables.every(function(key) {
    return dbInstance.schema.hasTable(key)
      .then(function(exists) {
        return exists;
      });
  });
}

无论如何你不会得到你想要的只是添加return。我会在第二点解释原因。

  1. Array.prototype.every 期望一个真值或假值 syncronouslydbInstance.schema.hasTable return 是一个 Promise 对象(和一个对象,即使为空,也始终为真)。

你现在要做的是检查表是否存在异步,我会告诉你如何:

var Promise = require("bluebird");
var schemaTables = ['posts', 'users', 'misc'];

function checkTable(tables, callback) {

  // I'm mapping every table into a Promise
  asyncTables = tables.map(function(table) {
    return dbInstance.schema.hasTable(table)
      .then(function(exists) {
        if (!exists)
          return Promise.reject("The table does not exists");
        return Promise.resolve("The table exists");
      });
  });

 // If all the tables exist, Promise.all return a promise that is fulfilled 
 // when all the items in the array are fulfilled.
 // If any promise in the array rejects, the returned promise 
 // is rejected with the rejection reason.
  Promise.all(asyncTables)
    .then(function(result) {
      // i pass a TRUE value to the callback if all the tables exist, 
      // if not i'm passing FALSE
      callback(result.isFulfilled());
    });

}


checkTable(schemaTables, function (result) {
  // here result will be true or false, you can do whatever you want
  // inside the callback with result, but it will be called ASYNCHRONOUSLY
  console.log(result);
});

请注意,正如我之前所说,您不能拥有同步 return 真值或假值的函数,因此您唯一能做的就是将回调传递给 checkTable将在结果准备就绪后立即执行(当所有承诺都实现或其中一个拒绝时)。 或者您可以 return Promise.all(asyncTables) 并在 checkTable 上自行调用 then,但我会把它留给您作为练习。 有关承诺的更多信息,请查看:

感谢 Cluk3 非常全面的回答。我实际上是通过使用 async 库中的 .every 方法自己解决的。但是,是的,这主要是由于我对 returns 和异步与同步的误解。

var checkTablesExist = function () {
    // make sure that all tables exist
    function checkTable(key, done) {
    dbInstance.schema.hasTable(key)
        .then(function(exists) {
            return done(exists);
            });
        }

    async.every(schemaTables, checkTable, 
        function(result) {
            return result;
        });
};

console.log(checkTablesExist());
// will now print true or false correctly