修复错误! (如何在同步函数中调用异步函数)

Fix the bug! (how to call an asynchronous function inside a synchronous function)

以下问题来自采访。我能够解决一些部分,但我卡住了:

修复以下代码中的错误。 DataContainer 是数据库的包装器,为从数据库中获取的数据提供缓存。

调用 fetch 应该通过 DbConnection 从数据库中获取数据。如果获取成功,它应该 return true;否则它应该 return false。调用 getData 应该 return 缓存数据或者如果数据还没有被获取则抛出异常。

function DataContainer(connectionString) {
  var dataFetched = false;
  var dbConnection = DbConnection(connectionString);
}

DataContainer.prototype.getData = function () {
  if (!this.dataFetched)
    throw "Data not fetched!";
  return this.data;
}

DataContainer.prototype.fetch = function () {
  this.dbConnection.getAllData(function (err, result) {
    if (err) {
      delete this.data;
      this.dataFetched = false;
      return false;
    } else {
      this.data = result;
      this.dataFetched = true;
      return true;
    }
  });
}

// Mock of DbConnection for testing purposes:
function DbConnection(connectionString) { }

DbConnection.prototype.getAllData = function (callback) {
  callback(null, [1, 2, 3]);
}

//Expected: 1, 2, 3
var dc = new DataContainer('connection');
if (dc.fetch()) {
  var data = dc.getData();
  for (var i = 0; i < data.length; i++) {
    console.log(data[i]);
  }
}

我尽可能地修复了代码,包括在构造函数中创建新的 DbConnection 对象,使用 this 关键字定义属性var 在构造函数中,并在调用 getData 时将 this 绑定到回调函数(查看以下代码)。唯一剩下的问题是如何在同步函数中调用异步函数(getData)?!

function DataContainer(connectionString) {
  this.dataFetched = false;
  this.dbConnection = new DbConnection(connectionString);
}

DataContainer.prototype.getData = function () {
  if (!this.dataFetched)
    throw "Data not fetched!";
  return this.data;
}

DataContainer.prototype.fetch = function () {
  this.dbConnection.getAllData(function (err, result) {
    if (err) {
      delete this.data;
      this.dataFetched = false;
      return false;
    } else {
      this.data = result;
      this.dataFetched = true;
      return true;
      }
    }.bind(this));
}

fetch是一个同步函数,我们不允许让它变成异步的。 getData是一个异步函数,我们不允许让它同步。现在我们如何在 fetch 中使用 getData???

fetch 是同步的,但是 getAllData 是异步的,所以问题是如何让 getAllData 表现得像一个同步函数。实现此目的的一种(也可能是唯一的)方法是使用 async/await:

DataContainer.prototype.fetch = async function fetch() {
  return await new Promise(resolve => {
      this.dbConnection.getAllData((err, result) => {
        if (err) {
          delete this.data;
          this.dataFetched = false;
        } else {
          this.data = result;
          this.dataFetched = true;
        }
        resolve(this.dataFetched);
      });
  });
}

这将允许消费代码将调用视为同步操作