indexedDB - использование обещания в конструкторе класса - Невозможно прочитать транзакцию свойства неопределенного [дубликата]

Если вы не хотите, чтобы все, что делится в другой структуре, вы должны использовать first-of-type и last-of-type вместо first-child и last-child

.area {
  height: 100px;
  width: 100px;
}

.area:first-of-type {
  background-color: red;
}

.area:last-of-type {
  background-color: green;
}
1
2
3
4

Так как Temani Afif указал, это решение произвольно и может не работать во всех ситуациях. Как показано, он неправильно работает над фрагментом кода, но, например, он работает на JSFiddle. И.Е. https://jsfiddle.net/vm1scerv/

116
задан Adam Beck 25 June 2014 в 03:53
поделиться

4 ответа

Да, это плохая практика. Конструктор должен возвращать экземпляр своего класса, ничего другого. Это испортило бы new operator и наследование в противном случае.

Кроме того, конструктор должен создавать и инициализировать новый экземпляр. Он должен настраивать структуры данных и все специфичные для экземпляра свойства, но не выполнять какие-либо задачи. Это должна быть чистая функция без побочных эффектов, если это возможно, со всеми преимуществами, которые имеют.

Что делать, если я хочу выполнять вещи из моего конструктора?

Это должно идти в методе вашего класса. Вы хотите изменить глобальное состояние? Затем вызовите эту процедуру явно, а не как побочный эффект создания объекта. Этот вызов может идти сразу после создания экземпляра:

var engine = new Engine()
engine.displayPosts();

Если эта задача асинхронна, теперь вы можете легко вернуть обещание своих результатов из метода, чтобы легко дождаться завершения. Однако я бы не рекомендовал этот шаблон, когда метод (асинхронно) мутирует экземпляр и другие методы, зависит от этого, поскольку это приведет к тому, что им потребуется ждать (стать асинхронным, даже если они фактически синхронны), и вы быстро некоторое внутреннее управление очередями продолжается.

Что делать, если я хочу асинхронно загружать данные в свой экземпляр?

Задайте себе вопрос: Вам действительно нужен экземпляр без данных? Не могли бы вы использовать его каким-то образом?

Если ответ на это No , то вы не должны создавать его, прежде чем у вас есть данные. Создайте данные, если это параметр для вашего конструктора, вместо того, чтобы сообщать конструктору, как извлекать данные (или передавать обещание для данных).

Затем используйте статический метод для загрузки данных, из которых вы возвращаете обещание. Затем соедините вызов, который обертывает данные в новом экземпляре:

Engine.load({path: '/path/to/posts'}).then(function(posts) {
    new Engine(posts).displayPosts();
});

Это позволяет значительно повысить гибкость в способах получения данных и значительно упростить конструктор. Аналогично, вы можете писать статические заводские функции, которые возвращают обещания для экземпляров Engine:

Engine.fromPosts = function(options) {
    return ajax(options.path).then(Engine.parsePosts).then(function(posts) {
        return new Engine(posts, options);
    });
};

…

Engine.fromPosts({path: '/path/to/posts'}).then(function(engine) {
    engine.registerWith(framework).then(function(framePage) {
        engine.showPostsOn(framePage);
    });
});
150
ответ дан ya_dimon 27 August 2018 в 07:22
поделиться

Возвращаемое значение конструктора заменяет объект, созданный новым оператором, поэтому возвращение обещания не является хорошей идеей. Раньше явное возвращаемое значение конструктора использовалось для шаблона singleton.

. Лучшим способом в ECMAScript 2017 является использование статических методов: у вас есть один процесс, который является числом статичных.

Какой метод запускать новый объект после конструктора может быть известен только самому классу. Чтобы инкапсулировать это внутри класса, вы можете использовать process.nextTick или Promise.resolve, откладывая дальнейшее выполнение, позволяющее добавлять слушателей и другие вещи в Process.launch, invoker конструктора.

Поскольку почти все коды выполняются внутри Promise, ошибки будут завершены в Process.fatal

. Эта основная идея может быть изменена в соответствии с конкретными потребностями в инкапсуляции.

class MyClass {
  constructor(o) {
    if (o == null) o = false
    if (o.run) Promise.resolve()
      .then(() => this.method())
      .then(o.exit).catch(o.reject)
  }

  async method() {}
}

class Process {
  static launch(construct) {
    return new Promise(r => r(
      new construct({run: true, exit: Process.exit, reject: Process.fatal})
    )).catch(Process.fatal)
  }

  static exit() {
    process.exit()
  }

  static fatal(e) {
    console.error(e.message)
    process.exit(1)
  }
}

Process.launch(MyClass)
0
ответ дан Harald Rudell 27 August 2018 в 07:22
поделиться

Я столкнулся с той же проблемой и придумал это простое решение.

Вместо того, чтобы возвращать Promise из конструктора, поместите его в свойство this.initialization, например:

function Engine(path) {
  var engine = this
  engine.initialization = Promise.resolve()
    .then(function () {
      return doSomethingAsync(path)
    })
    .then(function (result) {
      engine.resultOfAsyncOp = result
    })
}

Затем оберните каждый метод в обратном вызове, который запускается после инициализации, например:

Engine.prototype.showPostsOnPage = function () {
  return this.initialization.then(function () {
    // actual body of the method
  })
}

Как это выглядит с точки зрения потребителя API:

engine = new Engine({path: '/path/to/posts'})
engine.showPostsOnPage()

работает, потому что вы можете зарегистрировать несколько обратных вызовов для обещания, и они запускаются либо после его разрешения, либо, если он уже разрешен, во время присоединения обратного вызова.

Вот как mongoskin


Изменить: Поскольку я написал этот ответ, я влюбился в синтаксис ES6 / 7, поэтому есть и другой пример использования этого. Вы можете использовать его сегодня с babel.

class Engine {

  constructor(path) {
    this._initialized = this._initialize()
  }

  async _initialize() {
    // actual async constructor logic
  }

  async showPostsOnPage() {
    await this._initialized
    // actual body of the method
  }

}

Изменить: вы можете использовать этот шаблон изначально с помощью значка узла 7 и --harmony!

7
ответ дан phaux 27 August 2018 в 07:22
поделиться

Чтобы избежать разделения проблем, используйте фабрику для создания объекта.

class Engine {
    constructor(data) {
        this.data = data;
    }

    static makeEngine(pathToData) {
        return new Promise((resolve, reject) => {
            getData(pathToData).then(data => {
              resolve(new Engine(data))
            }).catch(reject);
        });
    }
}
3
ответ дан sth 27 August 2018 в 07:22
поделиться
Другие вопросы по тегам:

Похожие вопросы: