Асинхронный/Ждут Конструктора Класса

На данный момент, я'м пытаясь использовать асинхронный/ждут внутри функции конструктора класса. Это значит, что я могу сделать пользовательский электронной почты тег для проекта Электрон я'м работает на.

customElements.define('e-mail', class extends HTMLElement {
  async constructor() {
    super()

    let uid = this.getAttribute('data-uid')
    let message = await grabUID(uid)

    const shadowRoot = this.attachShadow({mode: 'open'})
    shadowRoot.innerHTML = `
      <div id="email">A random email message has appeared. ${message}</div>
    `
  }
})

Однако, на данный момент, проект не работает, со следующей ошибкой:

Class constructor may not be an async method

Есть ли способ обойти это, так что я могу использовать async/await не в этом? Вместо того, чтобы требовать обратных .тогда()?

Комментарии к вопросу (6)
Решение

Это может никогда работа.

В "асинхронный" по ключевым словам позволяет ждут, чтобы использоваться в функции, помеченные как "асинхронный", но он также преобразует функцию в генератор обещание. Поэтому функция помеченные асинхронный будет возвращать обещание. Конструктор с другой стороны возвращает объект строительства. Таким образом, мы имеем ситуацию, когда вы хотите вернуть объект и обещание: невозможная ситуация.

Вы можете только использовать асинхронный/ждут, где вы можете использовать слово, потому что они по сути синтаксис сахара для обещаний. Вы можете'т использовать обещания в конструктор, потому что конструктор должен возвращать объект должен быть построен, а не обещание.

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

  1. Использовать команду init() функция. Это работает немного как jQuery'ы .готов(). Объект, который вы создаете, может быть использована только внутри это's собственное init или готов функция:

Использование:

значения var myObj = новый класс MyClass(); myObj.инициализации(функция() { // здесь можно использовать myObj });

Реализация:

класса MyClass { конструктор () {

}

инит (обратного вызова) { // сделать что-то async и вызвать обратного вызова: обратного вызова.привязать(это)(); } }

  1. Использовать построитель. Я'вэ не видел этого используется много Javascript, но это один из самых распространенных обходных путей в Java, когда объект должен быть построен в асинхронном режиме. Конечно, шаблон Builder используется при строительстве объекта, который требует много сложных параметров. Что именно использовать-дело для асинхронных строителей. Разница в том, что асинхронный конструктор не возвращает объект, но обещание этого объекта:

Использование:

класса MyClass.построить().потом(функция(myObj) { // myObj возвращает обещание, // не конструктор // или застройщика });

// с асинхронный/ждут:

асинхронные функции foo () { расчет var myObj = ожидайте класса MyClass.создать(); }

Реализация:

класса MyClass { конструктор (async_param) { если (typeof на async_param === 'не определено') { бросьте новую ошибку('не может быть вызван напрямую'); } }

статическая сборка () { возвращение doSomeAsyncStuff() .потом(функция(async_result){ возвращение нового класса MyClass(async_result); }); } }

Реализация асинхронных/ждут:

класса MyClass { конструктор (async_param) { если (typeof на async_param === 'не определено') { бросьте новую ошибку('не может быть вызван напрямую'); } }

статические асинхронные сборки () { ВАР async_result = ждут doSomeAsyncStuff(); возвращение нового класса MyClass(async_result); } }

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


Обратите внимание на вызов функции внутри статической функции.

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

В это ключевое слово относится к созданному объекту. Не класса. Поэтому вы не можете нормально использовать " это " внутри статической функции после статической функции не привязаны к какой-либо объект, но и непосредственно привязана к классу.

То есть, в следующем коде:

class A {
    static foo () {}
}

Вы не можете сделать:

var a = new A();
a.foo() // NOPE!!

вместо этого вы должны назвать его как:

A.foo();

Таким образом, следующий код приведет к ошибке:

class A {
    static foo () {
        this.bar(); // you are calling this as static
                    // so bar is undefinned
    }
    bar () {}
}

Чтобы исправить это, вы можете сделать бар обычная функция или статический метод:

function bar1 () {}

class A {
    static foo () {
        bar1();   // this is OK
        A.bar2(); // this is OK
    }

    static bar2 () {}
}
Комментарии (9)

Вы можете сделать это. В основном:

class AsyncConstructor {
    constructor() {
        return (async () => {

            // All async code here
            this.value = await asyncFunction();

            return this; // when done
        })();
    }
}

чтобы создать класс использования:

let instance = await new AsyncConstructor();

Примечание: Если вам нужно использовать супер, вы не можете назвать его в асинхронный обратный вызов. Вы называете это вне его это не 100% идеально, но на мой взгляд это очень идиоматические и я использую его все время в мой код.

Комментарии (11)

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

Да, это означает, что, используя обещания, но это также означает "делать вещи таким же образом, как и любой другой HTML-элемент с", так что вы'вновь в хорошей компании. Например:

var img = new Image();
img.onload = function(evt) { ... }
img.addEventListener("load", evt => ... );
img.onerror = function(evt) { ... }
img.addEventListener("error", evt => ... );
img.src = "some url";

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

class EMailElement extends HTMLElement {
  constructor() {
    super();
    this.uid = this.getAttribute('data-uid');
  }

  setAttribute(name, value) {
    super.setAttribute(name, value);
    if (name === 'data-uid') {
      this.uid = value;
    }
  }

  set uid(input) {
    if (!input) return;
    const uid = parseInt(input);
    // don't fight the river, go with the flow
    let getEmail = new Promise( (resolve, reject) => {
      yourDataBase.getByUID(uid, (err, result) => {
        if (err) return reject(err);
        resolve(result);
      });
    });
    // kick off the promise, which will be async all on its own
    getEmail()
    .then(result => {
      this.renderLoaded(result.message);
    })
    .catch(error => {
      this.renderError(error);
    });
  }
};

customElements.define('e-mail', EmailElement);

И тогда вы сделаете функций renderLoaded/renderError реагировать на событие вызывает и тени Дом:

  renderLoaded(message) {
    const shadowRoot = this.attachShadow({mode: 'open'});
    shadowRoot.innerHTML = `
      <div class="email">A random email message has appeared. ${message}</div>
    `;
    // is there an ancient event listener?
    if (this.onload) {
      this.onload(...);
    }
    // there might be modern event listeners. dispatch an event.
    this.dispatchEvent(new Event('load', ...));
  }

  renderFailed() {
    const shadowRoot = this.attachShadow({mode: 'open'});
    shadowRoot.innerHTML = `
      <div class="email">No email messages.</div>
    `;
    // is there an ancient event listener?
    if (this.onload) {
      this.onerror(...);
    }
    // there might be modern event listeners. dispatch an event.
    this.dispatchEvent(new Event('error', ...));
  }

Также обратите внимание, я изменил свой ИДвкласс, потому что, если вы пишете какой-то странный код, чтобы только один экземпляр в<адрес электронной почты:>` элемент на странице, вы можете'т использовать уникальный идентификатор, а затем назначить его на кучу элементов.

Комментарии (0)

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

class Yql {
  constructor () {
    // Set up your class
  }

  static init () {
    return (async function () {
      let yql = new Yql()
      // Do async stuff
      await yql.build()
      // Return instance
      return yql
    }())
  }  

  async build () {
    // Do stuff with await if needed
  }
}

async function yql () {
  // Do this instead of "new Yql()"
  let yql = await Yql.init()
  // Do stuff with yql instance
}

yql()

Звонок с Пусть yql = ждут Yql.метод init() от асинхронной функции.

Комментарии (0)

использование асинхронного метода в построении???

constructor(props) {
    super(props);
    (async () => await this.qwe(() => console.log(props), () => console.log(props)))();
}

async qwe(q, w) {
    return new Promise((rs, rj) => {
        rs(q());
        rj(w());
    });
}
Комментарии (0)

Я сделал этот тест-кейсов на основе @Downgoat'ы ответ. Она работает на NodeJS. Это Downgoat's код, где асинхронных часть обеспечивается функции setTimeout() вызов.

'use strict';
const util = require( 'util' );

class AsyncConstructor{

  constructor( lapse ){
    this.qqq = 'QQQ';
    this.lapse = lapse;
    return ( async ( lapse ) => {
      await this.delay( lapse );
      return this;
    })( lapse );
  }

  async delay(ms) {
    return await new Promise(resolve => setTimeout(resolve, ms));
  }

}

let run = async ( millis ) => {
  // Instatiate with await, inside an async function
  let asyncConstructed = await new AsyncConstructor( millis );
  console.log( 'AsyncConstructor: ' + util.inspect( asyncConstructed ));
};

run( 777 );

Мой случай использования-это даос для серверной части веб-приложения. Как я вижу, даос, они каждый из которых связан с формат записи, в моем случае коллекции MongoDB, как например повар. В cooksDAO экземпляр держит повар's и данные. В мой беспокойный ум я хотел бы быть в состоянии создать экземпляр повар'с Дао, обеспечивающей cookId в качестве аргумента, и инстанцирование бы создать объект и заполнить его готовить's и данные. Таким образом, нужно запускать асинхронные вещи в конструкторе. Я хотел написать:

let cook = new cooksDAO( '12345' );  

иметь такие свойства, Как готовить.getDisplayName()`. С этим решением я должен сделать:

let cook = await new cooksDAO( '12345' );  

который очень похож на идеал. Кроме того, мне нужно сделать это внутри функции "асинхронный".

Мой Б-план был покинуть загрузка данных из конструктора, основанные на внушении @slebetman для использования функции init, и сделать что-то вроде этого:

let cook = new cooksDAO( '12345' );  
async cook.getData();

который не'т нарушать правила.

Комментарии (0)

Если вы не "расширить", вы сможете избежать занятия все вместе и использовать композицию функций как constructors. Вы можете использовать переменные в области, а не члены класса:

Яш асинхронные построить функцию(...) { данные const = ждут принести(...); возвращение { данные: функция() { возврат данных; } } }

и просто использовать его в качестве Яш const и А = ждут со многими(...);

Если вы'повторного использования TypeScript или поток, можно даже применить интерфейс constructors `` Интерфейс { данные: объект; }

асинхронные buildA0 функция(...): обещание<и> { ... } функция асинхронного buildA1(...): обещание<и> { ... } ... ``

Комментарии (0)

Вы можете создать асинхронный метод init() {... вернуть; метод}, то вместо этого нового класса MyClass().метод init() всякий раз, когда вы'д, Как правило, просто сказать новый класс MyClass().

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

Комментарии (0)

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

var message = (async function() { return await grabUID(uid) })()
Комментарии (0)

Вариация на шаблон строитель, с помощью call():

function asyncMethod(arg) {
    function innerPromise() { return new Promise((...)=> {...}) }
    innerPromise().then(result => {
        this.setStuff(result);
    }
}

const getInstance = async (arg) => {
    let instance = new Instance();
    await asyncMethod.call(instance, arg);
    return instance;
}
Комментарии (0)

@slebetmen'ы принято отвечать хорошо объясняет, почему это не'т работу. Помимо двух моделей, представленных в ответ, другой вариант-доступ только для асинхронных свойства через пользовательский асинхронный геттер. Конструктор() может вызвать асинхронного создания свойства, а получатель проверяет, чтобы увидеть, если свойство доступно, прежде чем он использует и возвращает его.

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

Использование

const instance = new MyClass();
const prop = await instance.getMyProperty();

Реализация

class MyClass {
  constructor() {
    this.myProperty = null;
    this.myPropertyPromise = this.downloadAsyncStuff();
  }
  async downloadAsyncStuff() {
    // await yourAsyncCall();
    this.myProperty = 'async property'; // this would instead by your async call
    return this.myProperty;
  }
  getMyProperty() {
    if (this.myProperty) {
      return this.myProperty;
    } else {
      return this.myPropertyPromise;
    }
  }
}
Комментарии (0)

Вы должны добавить, то функция к примеру. "Обещания" будут распознавать его как объект thenable собещание.разрешить автоматически

const asyncSymbol = Symbol();
class MyClass {
    constructor() {
        this.asyncData = null
    }
    then(resolve, reject) {
        return (this[asyncSymbol] = this[asyncSymbol] || new Promise((innerResolve, innerReject) => {
            this.asyncData = { a: 1 }
            setTimeout(() => innerResolve(this.asyncData), 3000)
        })).then(resolve, reject)
    }
}

async function wait() {
    const asyncData = await new MyClass();
    alert('run 3s later')
    alert(asyncData.a)
}
Комментарии (1)

Других ответов не хватает очевидного. Просто позвоните асинхронной функции из конструктора:

constructor() {
    setContentAsync();
}

async setContentAsync() {
    let uid = this.getAttribute('data-uid')
    let message = await grabUID(uid)

    const shadowRoot = this.attachShadow({mode: 'open'})
    shadowRoot.innerHTML = `
      <div id="email">A random email message has appeared. ${message}</div>
    `
}
Комментарии (5)