В данный момент я пытаюсь использовать async/await
функцию конструктора класса. Это сделано для того, чтобы я мог получить пользовательский e-mail
тег для проекта Electron, над которым я работаю.
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 в этом? Вместо того, чтобы требовать обратного вызова или .then ()?
javascript
node.js
async-await
Александр Краггс
источник
источник
<e-mail data-uid="1028"></email>
и оттуда заполняется информацией с использованиемcustomElements.define()
метода..init()
асинхронному. Кроме того, поскольку вы используете подкласс HTMLElement, весьма вероятно, что код, использующий этот класс, не знает, что это асинхронная вещь, поэтому вам, скорее всего, придется искать совершенно другое решение.Ответы:
Это никогда не может работать.
async
Ключевое слово позволяетawait
быть использовано в функции , помеченной как ,async
но он также преобразует эту функцию в генератор обещания. Таким образом, функция, отмеченная знакомasync
, вернет обещание. С другой стороны, конструктор возвращает объект, который он создает. Таким образом, у нас есть ситуация, когда вы хотите вернуть объект и обещание: невозможная ситуация.Вы можете использовать async / await только там, где можете использовать обещания, потому что они по сути являются синтаксическим сахаром для обещаний. Вы не можете использовать обещания в конструкторе, потому что конструктор должен возвращать конструируемый объект, а не обещание.
Для преодоления этого есть два конструктивных шаблона, оба были изобретены до того, как обещания были выполнены.
Использование
init()
функции. Это работает немного как JQuery.ready()
. Созданный вами объект можно использовать только внутри егоinit
илиready
функции:Использование:
Реализация:
Используйте строителя. Я не видел, чтобы это часто использовалось в javascript, но это один из наиболее распространенных обходных путей в Java, когда объект должен быть создан асинхронно. Конечно, шаблон строителя используется при построении объекта, который требует много сложных параметров. Это именно тот случай использования для асинхронных сборщиков. Разница в том, что асинхронный компоновщик возвращает не объект, а обещание этого объекта:
Использование:
Реализация:
Реализация с async / await:
Примечание о вызове функций внутри статических функций.
Это не имеет ничего общего с асинхронными конструкторами, но с тем, что на
this
самом деле означает ключевое слово (что может быть немного удивительно для людей, пришедших из языков, которые выполняют автоматическое разрешение имен методов, то есть языков, которымthis
ключевое слово не нужно ).this
Ключевое слово относится к экземпляру объекту. Не класс. Поэтому вы не можете нормально использоватьthis
внутри статических функций, так как статическая функция не привязана ни к какому объекту, а напрямую связана с классом.То есть в следующем коде:
Вы не можете сделать:
вместо этого вам нужно назвать это как:
Следовательно, следующий код может привести к ошибке:
Чтобы исправить это, вы можете сделать
bar
обычную функцию или статический метод:источник
init()
но имеет функциональность, привязанную к какому-либо конкретному атрибуту, напримерsrc
илиhref
(и в данном случае,data-uid
), что означает использование установщика, который одновременно связывает и запускает инициализацию каждый раз, когда связывается новое значение (и, возможно, также во время конструирования, но, конечно, без ожидания пути к результирующему коду)bind
требуется в первом примереcallback.bind(this)();
? Так что вы можете делать такие вещи, какthis.otherFunc()
в обратном вызове?this
в обратном вызове относитсяmyClass
. Если вы всегда используетеmyObj
вместоthis
вас, это не нужноconst a = await new A()
же, как у нас есть обычные функции и асинхронные функции.Вы определенно можете сделать это. В принципе:
для создания класса используйте:
Это решение имеет несколько недостатков:
источник
constructor(x) { return (async()=>{await f(x); return this})() }
return this
необходим, потому что, хотяconstructor
он делает это автоматически для вас, этого не делает асинхронный IIFE, и вы в конечном итоге вернете пустой объект.T
должен возвращатьсяT
при создании, но чтобы получить возможность асинхронности, которую мы возвращаем,Promise<T>
которая разрешаетсяthis
, но это сбивает с толку машинопись. Вам действительно нужен внешний возврат, иначе вы не будете знать, когда обещание завершится - в результате этот подход не будет работать с TypeScript (если только не будет хака с возможно псевдонимом типа?). Не специалист по машинописи, хотя и не могу говорить об этомПоскольку асинхронные функции являются обещаниями, вы можете создать статическую функцию в своем классе, которая выполняет асинхронную функцию, которая возвращает экземпляр класса:
Вызов с
let yql = await Yql.init()
асинхронной функцией.источник
Основываясь на ваших комментариях, вы, вероятно, должны делать то, что делает любой другой HTMLElement с загрузкой ресурсов: заставить конструктор запустить действие боковой загрузки, генерируя событие загрузки или ошибки в зависимости от результата.
Да, это означает использование обещаний, но это также означает, что «все делается так же, как и любой другой элемент HTML», так что вы находитесь в хорошей компании. Например:
это запускает асинхронную загрузку исходного ресурса, которая, в случае успеха, заканчивается,
onload
а когда идет не так, заканчиваетсяonerror
. Итак, сделайте так, чтобы ваш собственный класс тоже делал это:А затем вы заставляете функции renderLoaded / renderError обрабатывать вызовы событий и shadow dom:
Также обратите внимание, что я изменил ваш
id
на aclass
, потому что, если вы не напишите какой-то странный код, разрешающий только один экземпляр вашего<e-mail>
элемента на странице, вы не сможете использовать уникальный идентификатор, а затем назначить его группе элементов.источник
Я сделал этот тест на основе ответа @ Downgoat.
Он работает на NodeJS. Это код Downgoat, в котором асинхронная часть обеспечивается
setTimeout()
вызовом.Мой пример использования - это DAO для серверной части веб-приложения.
Как я вижу DAO, каждый из них связан с форматом записи, в моем случае это коллекция MongoDB, например, повар.
Экземпляр cooksDAO содержит данные повара.
В своем беспокойном уме я мог бы создать экземпляр DAO повара, предоставив в качестве аргумента идентификатор cookId, а экземпляр создал бы объект и наполнил бы его данными повара.
Таким образом, необходимо запустить асинхронный материал в конструкторе.
Я хотел написать:
иметь доступные свойства, такие как
cook.getDisplayName()
.С этим решением мне нужно сделать:
что очень похоже на идеал.
Кроме того, мне нужно сделать это внутри
async
функции.Мой B-план состоял в том, чтобы исключить загрузку данных из конструктора, основываясь на предложении @slebetman использовать функцию init и сделать что-то вроде этого:
который не нарушает правила.
источник
использовать асинхронный метод в конструкции ???
источник
Решение для временной задержки
Вы можете создать
async init() {... return this;}
метод, а затем делатьnew MyClass().init()
всякий раз, когда вы обычно говоритеnew MyClass()
.Это не чисто, потому что оно зависит от всех, кто использует ваш код, и от вас самих, чтобы всегда создавать экземпляр объекта таким образом. Однако, если вы используете этот объект только в определенном месте или двух в своем коде, это может быть хорошо.
Однако возникает серьезная проблема, потому что в ES нет системы типов, поэтому, если вы забудете вызвать ее, вы только что вернулись
undefined
потому что конструктор ничего не возвращает. К сожалению. Намного лучше было бы сделать что-то вроде:Лучшее, что можно сделать:
Фабричный метод решения (чуть лучше)
Однако тогда вы можете случайно сделать новый AsyncOnlyObject, вам, вероятно, следует просто создать фабричную функцию, которая использует
Object.create(AsyncOnlyObject.prototype)
напрямую:Однако, скажем, вы хотите использовать этот шаблон на многих объектах ... вы можете абстрагировать его как декоратор или что-то, что вы (многословно, тьфу) вызываете после определения like
postProcess_makeAsyncInit(AsyncOnlyObject)
, но здесь я собираюсь использовать,extends
потому что он как бы вписывается в семантику подкласса. (подклассы являются родительским классом + дополнительный, в том смысле, что они должны подчиняться контракту разработки родительского класса и могут делать дополнительные вещи; асинхронный подкласс был бы странным, если бы родитель не был также асинхронным, потому что его нельзя было инициализировать одинаково путь):Абстрактное решение (расширяет / версия подкласса)
(не используйте в производстве: я не продумал сложные сценарии, например, является ли это правильным способом написания оболочки для аргументов ключевых слов.)
источник
В отличие от других, вы можете заставить его работать.
JavaScript
class
может возвращать буквально все что угодноconstructor
, даже экземпляр другого класса. Таким образом, вы можете вернутьPromise
конструктор вашего класса, который разрешает его фактический экземпляр.Ниже приведен пример:
Затем вы создадите экземпляры
Foo
следующим образом:источник
Если вы можете избежать этого
extend
, вы можете избежать всех классов вместе и использовать композицию функций в качестве конструкторов . Вы можете использовать переменные в области вместо членов класса:и просто использовать его как
Если вы используете машинописный текст или поток, вы можете даже применить интерфейс конструкторов
источник
Изменение шаблона построения с использованием call ():
источник
Вы можете немедленно вызвать анонимную асинхронную функцию, которая возвращает сообщение, и установить для нее переменную сообщения. Возможно, вы захотите взглянуть на немедленно вызванные выражения функций (IEFES), если вы не знакомы с этим шаблоном. Это будет работать как шарм.
источник
Принятый ответ @ slebetmen хорошо объясняет, почему это не работает. В дополнение к двум шаблонам, представленным в этом ответе, другой вариант - получить доступ к вашим асинхронным свойствам только через пользовательский асинхронный геттер. Затем конструктор () может инициировать асинхронное создание свойств, но получатель затем проверяет, доступно ли свойство, прежде чем оно использует или возвращает его.
Этот подход особенно полезен, когда вы хотите инициализировать глобальный объект один раз при запуске, и вы хотите сделать это внутри модуля. Вместо инициализации в вашем
index.js
и передачи экземпляра в местах, где это нужно, простоrequire
ваш модуль, где нужен глобальный объект.использование
Реализация
источник
Другие ответы упускают очевидное. Просто вызовите асинхронную функцию из вашего конструктора:
источник
this.myPromise =
(в общем смысле) в любом смысле не быть анти-паттерном. Существуют совершенно обоснованные случаи необходимости запуска асинхронного алгоритма при создании, который сам по себе не имеет возвращаемого значения, и добавления одного простого значения в любом случае, так что тот, кто советует не делать этого, что-то неправильно понимаетВы должны добавить
then
функцию к экземпляру.Promise
распознает его как доступный объект сPromise.resolve
автоматическиисточник
innerResolve(this)
не будет работать, какthis
это все еще возможно. Это приводит к бесконечному рекурсивному разрешению.