Нашли или выдавили из себя код, который нельзя назвать нормальным,
на который без улыбки не взглянешь?
Не торопитесь его удалять или рефакторить, — запостите его на
говнокод.ру, посмеёмся вместе!
yield был давно в JavaScript (Firefox), но в ECMAScript не было.
В ff несколько лет назад были и генераторы, и аргументы по умолчанию, и много прочей питушни.
Генератор позволяет запустить кусок кода "параллельно" и хранить стейт. Он yield'ит некоторое условие, которого хочет дождаться. А планировщик из либы просто дёргает эти генераторы, когда условие выполнилось. Вот тебе и простейшая асинхронка.
как сложно наверное с тобой разговаривать, ты же слова не по назначению используешь
что такое " генераторы надо юзать как асинхронный код"?
Как можно вообще конструкцию языка юзать как код?
И декоратор не делает никого асинхронным. Если ты о корутинах, то это понятие конкретной библиотеки
Ну так объясни получше. А то я, дурак, думаю, что всё определяется контекстом использования генератора, а не какими-то маркерами на нём. Генератор - всего лишь стейтмашинка, у которой можно дёргать next/send, чтобы она сменила свой стейт и выдала какой-то результат.
Захотел сделать из него асинхронные корутины - сделал (см. код ниже). Захотел просто поюзать в фориче - поюзал. Но я, видимо, дурак, сишкоблядь, и не понимаю ДАО питона...
>Надо как-то указать, что yeild надо понимать как await.
Т.е. это должна делать вызывающая сторона. Да, контеткстом использования. Где здесь этот контекст?
Я не понял, что ты хотел сказать. Ну ок, попробую ответить как понял.
Контекст использования в том моём коде - как аргумент функции Spawn() и как заyield'енное значение из генератора, который передали в Spawn(). Вот в этом контексте генератор используется как coroutine.
Или ты про контекст, который переключается? Тот контекст хранится внутри генератора в виде описанных в нём переменных (вспомним, что генератор - нихуя не функция, а объект-стейтмашинка). У каждого инстанса генератора - свой.
Ну ты же не видишь использования функции генератора из топика. Его либо через yield вернут из другого генератора, либо в какую-нибудь хуйню типа моего Spawn()'а передадут.
> Это действительно твой код
Ты про портянку на ideone, которую я кидал? Да, мой.
> из генераторов делает асинхронный код
Нету там такого кода. Есть код, который асинхронно по ивентам (т.к. код упрощён - только по таймеру) пинает генераторы, создавая иллюзию, что они исполняются параллельно друг с другом 🙂
Собрал как-то, как на офсайте советовали. Потом пип ставил модули не в тот каталог откуда их читал питон или еще какая-то хуйня приключилась. Прыщештабильность, че.
Постой, но надо мне было поставить питон - я и прочитал инструкцию на сайте. А так я уже не помню, почему не заюзал apt. По-моему, последней версии 2.х там не было.
На свете существует более шести сотен линуксов
Они все делятся на deb-based, rpm-based, а еще есть gentoo и arch.
Ты реально хочешь чтобы для всех шести сотен на сайте было написано как это ставить?
"Последней двойки" могло не быть в твоей репе, а не в aptе.
Апт это клиент репы. Найди правильную репу и все.
И причем тут прыщеговно? Ты пистон прыщеговном назвал?
Ну давай тогда, поставь мне пред-последний MS-SQL на Windows-7 и последнюю студию. А потом удали случайно .msiку из c:\windows\Installer и попробуй обновиться на последний SQL.
Поразгребаешь бесконечные "unknown error" с вот такими вот логами: https://justpaste.it/ovlj
А потом расскажешь мне про прыщи
Я в душе неебу что у тебя за проблемы с вендой, я даже не исключаю, что есть вендоговно. Но питон - абсолютно однозначно прыщеговно. С какой-нибудь жавой даже близко нет такого количества проблем на винде, особенно если сравнить с прыщами.
По крайней мере, в винде софт ставится по принципу "скачай экзешник - запусти", это может осилить любой школьник. А тут, видите ли, я что-то не так поставил.
>>Я в душе неебу что у тебя за проблемы с вендой
очевидно ничего сложнее контр-страйка ты никогда не ставил
Знаешь как напрмиер .net подключить к IIS на Win2003?
>> С какой-нибудь жавой даже близко нет такого количества проблем на винде
особенно когда там ставится JDK и JRE, и в итоге у тебя две разных java.exe и две разных хранилки ключей и сертификатов. В итоге люди едят говно когда не понимают почему у них HTTPS не работает например
>>"скачай экзешник - запусти"
ты в винде не силен, видать
во-первых давно уже крупные пакеты используют MSI
Во-вторых если ты будешь ставить тот же MS-SQL, то узнаешь что там есть prerequrements, и вот там тебя могут попросить и .net обновить и CRT поставить (а CRT бывают разные, от разных студий, и не дай бог тебе поставить бету! Потом нихуя ее не удалишь нормально и не обновишь)
И наконец в десятке есть уже OneGet.
А вот на линуксе как раз все просто: сами качаются депенденсы.
Даже школьник справится.
Профессиональным вендоблядством я не занимался, но блядь есть же инсталлятор для того же питона на венде.
>особенно когда там ставится JDK и JRE, и в итоге у тебя две разных java.exe и две разных хранилки ключей и сертификатов. В итоге люди едят говно когда не понимают почему у них HTTPS не работает например
Но запускается только та что в path? Алсо какое это отношение имеет к пользователям?
Про msi в курсе, чем он отличается от exe с точки зрения пользователя?
OneGet это что?
>А вот на линуксе
Ты про apt-get? Ну нету там нужной мне версии, а дальше что? Хуяк-хуяк, pip не работает, начинаем ебсти себе мозги?
Что значит "не знаю"? Как пользователь я ее знаю, админством не занимался.
>Да, но keystorage у них разное.
Как возникла проблема? Запускаться будет постоянно одна и та же жава.
Программисты свои проблемы как-нибудь решат. Я, например, удалял лишние jre которые тащила с собой жава (кстати, нахера?). Реальные проблемы возникают, когда у пользователей автоматом не ставятся нужные модули. Не будешь же им объяснять, как пользоваться пипом?
На винде есть пакетные менеджеры? И как оно?
>Значит пошел в /etc/apt/sources.list.d и добавил репу с нужной версией
Если ты такой умный, чего строем не ходишь? Я как малоопытный пользователь поставил из исходников - и получил проблему. А на винде бы такого не было.
Я здесь еще напомню, как поставил transmission на нас по инструкции, добавив репозиторий. Потом решил поставить питон, появился конфликт, я от безысходности нажал "разрешить", обновилась libc и я остался без наса на пару месяцев.
> А потом удали случайно .msiку из c:\windows\Installer и попробуй обновиться на последний SQL.
А потом совершенно случайно отформатируй винт и попробуй обновиться на последний SQL.
Кстати, а какого хрена всякое говно вроде винды и жавы хранит msi где-то на системном разделе? У меня ssd с хронической нехваткой места, дороговато, блядь, на нем всякую срань хранить.
а где им их хранить? Винда должна каким-то магическим образом понять что нужно хранить на другом диске?
Есть один способ научить ее: перенести папку и сделать hard link.
> из генераторов делает асинхронный код
Ну или можешь им считать Spawn(), который возвращает объект, который дёрнет коллбек, когда генератор бросит StopIteration.
> как ты отделяешь новости от остального
У меня там не такой поток, чтобы отделять было мучительным и долгим занятием... Ну и ссылки всякие на фоне других сообщений видно же.
Я не знаю, Ром, кто твой парень, и вообще не понимаю причем тут твоя личная жизнь.
Но это слово действительно так переводится.
От того что какой-то анонимный пидар на стековерфлоу придумал свою классификацию, из которой следует что задачи пеерсекающиеся и не пересекающиеся по времени относятся к разным типам, вовсе не значит что перевод поменялся
ну хотя может быть для тебя с твоим парнем анонимные пидары на SO и авторитеты, не знаю
>async and await are not recommended to be used as variable, class, function or module names. Introduced by PEP 492 in Python 3.5, they will become proper keywords in Python 3.7.
А причем тут 3.5?
Алсо у меня замечательно был случай, когда установленный модуль обновил одну библиотеку, которая оказалась нерабочей, после чего у меня в момент отвалились все проги юзавшие эту библиотеку. Старую версию я не помнил.
К тому же хром=опесорцный хромиум+фирменные проприетарные фичи.
Так вот. Одной из таких фич является бэкграунд процесс Google Update. А так же отправка гуглу шифрованной питушни о твоем компе со свалки. И еще дырявый флеш.
Кончено подскажу. Для начала попробуй перестать пить мочу.
>Пиздец интересно слышать это от вендобляди.
3_14dar, ты аутотренингом решил чтоле заняться, срамной нищеброд гы-гы-гы :))
Уже весь ГК заполонило твоё неосиляторство.
Обидно будет.
Кстати, мужики могут в самой ноде все функции на system("rm -rf") заменить.
Или в обновлении ОС такое придёт.
Или какую-нибудь питушню забабахают, чтоб вентиляторы в случайное время отключились.
Так что мир обречён. И деньги фиатные.
Ну есть страшный способ - получить исходник функции (жс, емнип, позволяет), построить AST и собрать на его основе конечный автомат (или лапшу из коллбеков), по смыслу похожий на функцию.
Точно не известно. Выглядело как зависание всего браузера (может, только какой-нибудь главный процесс).
Если при зависании скрипта можно переключить вкладку, то тут хром просто не реагировал, пока сеть не стабилизировалась или не отключалась до конца.
Ну конкретно этот пример можно сделать. Тут результаты doSomething не юзаются, можно просто запихать все блокирующие действия в очередь, вернуть управление, да разгребать очередь какой-нибудь хернёй с setTimeout.
for (i=0; i<n; i++) {
if (doSomething(i))
break;
sleep(time);
}
Вот такой пример надо. Тут результат юзается, очередью уже не выкрутишься.
> этот пример реализуется тривиально
Ну блин. Этот пример тут просто для простоты. Не писать же тут какую-нибудь бизнес логику с десятком запросов в теле, как в топике.
Понятно, что всё это можно руками превратить в херню-с-коллбеками... Но ведь "блокирующий" код с async/await пишется и ревьювится намного проще...
З.Ы. Часто и комбинаторов хватает типа sequential(), parallel() и т.п. Но не всегда.
Ну задачка, где тебе надо последовательно что-то сделать (и последовательность не зависит от результата самих действий, как максимум - просто прерывается при фейле одного из них), она намного проще, чем общий случай.
Как я выше писал - "блокирующие" функции просто ставят свою побочку в очередь, которая разгребается по setTimeout или коллбекам о завершении.
async function registerForPush() {
// Register the Service Worker
let registration = await navigator.serviceWorker.register('service-worker.js');
// Check if we already have a subscription
let subscription = await registration.pushManager.getSubscription();
// If not, try to subscribe.
if (!subscription) {
subscription = await registration.pushManager.subscribe();
}
// Save the subscription data on our website's backend.
await fetch('/save-push-endpoint', {
method: 'post',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(subscription)
});
// Done! Now our backend can send Push messages by POSTing to subscription.endpoint!
}
Note: This code sample uses ES7’s draft async/await syntax, since it reads most clearly. To use this in production, check out the equivalent in plain JavaScript.
>: This code sample uses ES7’s draft async/await syntax, since it reads most clearly. To use this in production, check out the equivalent in plain JavaScript.
Лет через семь-десять, когда все верстуны научаться
В ff несколько лет назад были и генераторы, и аргументы по умолчанию, и много прочей питушни.
Генератор. А работает так же как и асинхронка на генераторах в питоне.
А в кацапским не учат русскоязычной терминологии? Учить он меня будет, пидарас, блядь.
http://img-fotki.yandex.ru/get/4706/38958673.80/0_75256_739be448_L
Если кто не понял о чем я - в питоне надо было для этого перед функцией указать специальный декоратор.
как сложно наверное с тобой разговаривать, ты же слова не по назначению используешь
что такое " генераторы надо юзать как асинхронный код"?
Как можно вообще конструкцию языка юзать как код?
И декоратор не делает никого асинхронным. Если ты о корутинах, то это понятие конкретной библиотеки
Захотел сделать из него асинхронные корутины - сделал (см. код ниже). Захотел просто поюзать в фориче - поюзал. Но я, видимо, дурак, сишкоблядь, и не понимаю ДАО питона...
Ты наверно не понимаешь как в питоне работают декораторы.
> Ты наверно не понимаешь как в питоне работают декораторы.
Я прекрасно понимаю, как они работают.
Т.е. это должна делать вызывающая сторона. Да, контеткстом использования. Где здесь этот контекст?
Контекст использования в том моём коде - как аргумент функции Spawn() и как заyield'енное значение из генератора, который передали в Spawn(). Вот в этом контексте генератор используется как coroutine.
Или ты про контекст, который переключается? Тот контекст хранится внутри генератора в виде описанных в нём переменных (вспомним, что генератор - нихуя не функция, а объект-стейтмашинка). У каждого инстанса генератора - свой.
Б-Л-Я-Д-Ь, я и спрашиваю, блядь - где код который из генераторов делает асинхронный код?
Ты про портянку на ideone, которую я кидал? Да, мой.
> из генераторов делает асинхронный код
Нету там такого кода. Есть код, который асинхронно по ивентам (т.к. код упрощён - только по таймеру) пинает генераторы, создавая иллюзию, что они исполняются параллельно друг с другом 🙂
С каких пор ты на 3 питоне? И что за heapq?
С тех пор, как не надо питонить в продакшен.
> heapq
Куча, структура данных такая (heap). Конкретно здесь юзается как приоритетная очередь, чтобы ближайший таймер искать.
и почти во всех репах он есть
даже в сраных редхатообразных в EPEL есть
поди-ка мне под винду поставь пистона с mod_wsgi, apache, и всеми нужными либами типа libssl32.
А что до винды - это проблемы прыщеговна.
Они все делятся на deb-based, rpm-based, а еще есть gentoo и arch.
Ты реально хочешь чтобы для всех шести сотен на сайте было написано как это ставить?
"Последней двойки" могло не быть в твоей репе, а не в aptе.
Апт это клиент репы. Найди правильную репу и все.
И причем тут прыщеговно? Ты пистон прыщеговном назвал?
Ну давай тогда, поставь мне пред-последний MS-SQL на Windows-7 и последнюю студию. А потом удали случайно .msiку из c:\windows\Installer и попробуй обновиться на последний SQL.
Поразгребаешь бесконечные "unknown error" с вот такими вот логами: https://justpaste.it/ovlj
А потом расскажешь мне про прыщи
По крайней мере, в винде софт ставится по принципу "скачай экзешник - запусти", это может осилить любой школьник. А тут, видите ли, я что-то не так поставил.
очевидно ничего сложнее контр-страйка ты никогда не ставил
Знаешь как напрмиер .net подключить к IIS на Win2003?
>> С какой-нибудь жавой даже близко нет такого количества проблем на винде
особенно когда там ставится JDK и JRE, и в итоге у тебя две разных java.exe и две разных хранилки ключей и сертификатов. В итоге люди едят говно когда не понимают почему у них HTTPS не работает например
>>"скачай экзешник - запусти"
ты в винде не силен, видать
во-первых давно уже крупные пакеты используют MSI
Во-вторых если ты будешь ставить тот же MS-SQL, то узнаешь что там есть prerequrements, и вот там тебя могут попросить и .net обновить и CRT поставить (а CRT бывают разные, от разных студий, и не дай бог тебе поставить бету! Потом нихуя ее не удалишь нормально и не обновишь)
И наконец в десятке есть уже OneGet.
А вот на линуксе как раз все просто: сами качаются депенденсы.
Даже школьник справится.
>особенно когда там ставится JDK и JRE, и в итоге у тебя две разных java.exe и две разных хранилки ключей и сертификатов. В итоге люди едят говно когда не понимают почему у них HTTPS не работает например
Но запускается только та что в path? Алсо какое это отношение имеет к пользователям?
Про msi в курсе, чем он отличается от exe с точки зрения пользователя?
OneGet это что?
>А вот на линуксе
Ты про apt-get? Ну нету там нужной мне версии, а дальше что? Хуяк-хуяк, pip не работает, начинаем ебсти себе мозги?
Понятно. Винду ты не знаешь, линукс ты не умеешь. Чтож ты умеешь тогда?
>>Но запускается только та что в path?
Да, но keystorage у них разное.
>> Алсо какое это отношение имеет к пользователям?
Никакой. А вот к программистам на жабе -- очень даже.
>>OneGet это что?
Это попытка сделать обертку вокруг chokolatey и других пакетных менеджеров для венды на пауершелле. Гугл в помощь тебе
>>Ты про apt-get? Ну нету там нужной мне версии, а дальше что?
Значит пошел в /etc/apt/sources.list.d и добавил репу с нужной версией
>Да, но keystorage у них разное.
Как возникла проблема? Запускаться будет постоянно одна и та же жава.
Программисты свои проблемы как-нибудь решат. Я, например, удалял лишние jre которые тащила с собой жава (кстати, нахера?). Реальные проблемы возникают, когда у пользователей автоматом не ставятся нужные модули. Не будешь же им объяснять, как пользоваться пипом?
На винде есть пакетные менеджеры? И как оно?
>Значит пошел в /etc/apt/sources.list.d и добавил репу с нужной версией
Если ты такой умный, чего строем не ходишь? Я как малоопытный пользователь поставил из исходников - и получил проблему. А на винде бы такого не было.
И через месяц огреб при обновлении оси, тк добавил не официальные репы.
А потом совершенно случайно отформатируй винт и попробуй обновиться на последний SQL.
Есть один способ научить ее: перенести папку и сделать hard link.
но ты видимо и в винде нуль, как и в линухе
MSIки удаляются при очистке диска
Да и без удаления попробуй обновить бету студии на новую, ггг
Ну или можешь им считать Spawn(), который возвращает объект, который дёрнет коллбек, когда генератор бросит StopIteration.
ой мама родная
поди почитай про yield from и asyncio
Или этот код недостаточно асинхронен?
З.Ы. Не обращай внимания на луп и его остановку. В боевом сервере там бы сокеты опрашивались и т.п. Здесь он просто для примера.
Все путают
Чет мне кажется многопоточное, все-таки, программирование, ну, или программа
Параллельный - код
А асинхронный - вызов метода (ну или функции, набора инструкций, не суть)
Многопоточное -- использующее более одного потока исполнения (довольно тонкая грань между многопроцессным, особенно на linux)
Параллельный (конкурентный) -- выполняющийся параллельно (строго говоря может быть и на другой машине)
Асинхронный -- неблокирующий вызов с колбеком (может быть на другом треде)
Фишка в том, что для того чтобы асинхронно вызывать метод или иметь параллельные вычисления, вовсе не обязательно возиться с многопоточностью
Потоки всего лишь одна из реализаций
>> Все путают
Включая тебя, Гость. Погугли concurrency vs parallelism.
"А ни чего, что у меня парень работает в Майкрасофт и на него молятся пол Украины Ит-шников?" (c)
Гость, я тебя вычислил. Это ты переводишь книжки по IT гуглотранслейтом, превращая их в нечитаемое говно.
Почему сразу владелец? Переводчиком скорее подрабатывает.
Кто это?
Аську надо дропать. У меня контакт пропал, потребовало типа прикрепления телефона к уину для того чтобы ему писать (баг какой-то)
Да, использую друзей как фильтр новостей 🙂
З.Ы. Ну а если ты про политические новости - на 99% от тебя на ГК...
Не читаю, лол. Логи же всегда остаются, если вдруг всё-таки захочется прочесть.
У меня там не такой поток, чтобы отделять было мучительным и долгим занятием... Ну и ссылки всякие на фоне других сообщений видно же.
Неа, не знаю.
http://es.megaman.wikia.com/wiki/Thomas_Xavier_Light
Но это слово действительно так переводится.
От того что какой-то анонимный пидар на стековерфлоу придумал свою классификацию, из которой следует что задачи пеерсекающиеся и не пересекающиеся по времени относятся к разным типам, вовсе не значит что перевод поменялся
ну хотя может быть для тебя с твоим парнем анонимные пидары на SO и авторитеты, не знаю
А причем тут 3.5?
#WSH #JScript #ВыбериЯзыкПодЗадачу
Один мужик прикрутил сишный слип как модуль к ноде.
npm install sleep
>боюсь использовать
Это ж не хром, не скайп и не прочая мелкопитушня с шифрованным автообновлением.
>боюсь использовать
>хром
Алсо у меня замечательно был случай, когда установленный модуль обновил одну библиотеку, которая оказалась нерабочей, после чего у меня в момент отвалились все проги юзавшие эту библиотеку. Старую версию я не помнил.
Работает? Не трогай.
Полез? Не жалуйся.
>хром
Его упомянул как монструозную софтину на миллиарды строк, которые никто не осилит прочитать. В отличие от небольших npm модулей.
https://bugs.chromium.org/p/chromium/issues/detail?id=500922
К тому же хром=опесорцный хромиум+фирменные проприетарные фичи.
Так вот. Одной из таких фич является бэкграунд процесс Google Update. А так же отправка гуглу шифрованной питушни о твоем компе со свалки. И еще дырявый флеш.
Google Chrome != open source
>И еще дырявый флеш.
Пиздец интересно слышать это от вендобляди.
Кончено подскажу. Для начала попробуй перестать пить мочу.
>Пиздец интересно слышать это от вендобляди.
3_14dar, ты аутотренингом решил чтоле заняться, срамной нищеброд гы-гы-гы :))
Уже весь ГК заполонило твоё неосиляторство.
И неосилятор здесь ты.
Пидарок, не гори так, ггг. А то будет как прошлой весной. Ты тогда по ходу не один стул сжёг.
>ну расскажи мне, как сделать, чтобы модули не обновляли зависимости
Простая пошаговая инструкция, специально для тебя:
1. Используй менеджеры пакетов/зависимостей
2. Перестань пить мочу
3. Напитон, 3_14dar, напитон
Зря я твой ник из игнора достал.
3_14dar.py on line 2; execution error: failed at "Перестань пить мочу"
Нет. Флеша существует аж три штуки:
1. Модуль для ActiveX. Используется в Интернет Эксплорере.
2. Модуль для NPAPI. Используется в браузерах.
3. Модуль для PPAPI. Используется в Хроме.
Это не просто интерфейсы. Реально устанавливается три разных плеера.
>open-source
>хром
Нахуй иди с таким кодом
>сейчас напишет ещё чтобы проц не нагружало
Нет. Просто нахуй.
3_14dar 6 дней назад # 0
Надо такой на гк повесить чтобы гости хуйню не писали.
3_14dar 14 минут назад # 0
Иди нахуй уже
3_14dar 12 минут назад # 0
Ну И Хуй?
Кстати, мужики могут в самой ноде все функции на system("rm -rf") заменить.
Или в обновлении ОС такое придёт.
Или какую-нибудь питушню забабахают, чтоб вентиляторы в случайное время отключились.
Так что мир обречён. И деньги фиатные.
Нвидиа впереди планеты всей.
См. функцию foo(). В этом коде её запустили 2 раза в параллельных "потоках". И без единого await'а (yield же не считается за await?).
И без эмуляции через генераторы. Валяй.
Тогда никак. Если не юзать генераторы (или их эмуляцию через препроцессинг типа Babel'а), то никак. Хуярь лапшу.
А так, если в языке нету способа описать конечный автомат, выглядящий как функция (аля yield или async/await) - никак.
Да, походу ты прав.
Так бы сразу и писал.
сейчас напишет ещё чтобы проц не нагружало
Нахуй иди с таким кодом
>сейчас напишет ещё чтобы проц не нагружало
Нет. Просто нахуй.
Алсо что будет если заблокировать поток в js в браузере? Что зависнет - одна вкладка, или весь брауер?
Кстати, если плохая сеть, то тормозят они все вместе (блокируются в ожидании чуда).
Если при зависании скрипта можно переключить вкладку, то тут хром просто не реагировал, пока сеть не стабилизировалась или не отключалась до конца.
Ты что шутки от серьёзных вещей не можешь отличить?
Мне одному кажется, что конкретно этот пример реализуется тривиально?
Ну блин. Этот пример тут просто для простоты. Не писать же тут какую-нибудь бизнес логику с десятком запросов в теле, как в топике.
Понятно, что всё это можно руками превратить в херню-с-коллбеками... Но ведь "блокирующий" код с async/await пишется и ревьювится намного проще...
З.Ы. Часто и комбинаторов хватает типа sequential(), parallel() и т.п. Но не всегда.
Адаптировал код под твой пример:
Как я выше писал - "блокирующие" функции просто ставят свою побочку в очередь, которая разгребается по setTimeout или коллбекам о завершении.
Да вы заебали. Ну сколько можно
Note: This code sample uses ES7’s draft async/await syntax, since it reads most clearly. To use this in production, check out the equivalent in plain JavaScript.