Цифровий феодалізм. Чи може ШІ скасувати «нове кріпацтво»?

Цифровий феодалізм

Гаспар Кеніг – французький письменник і філософ, засновник руху Simple, який виступає за радикальне спрощення бюрократичного апарату держави. Працюючи над книгою «Кінець індивідуума», він зустрівся з десятками фахівців у галузі штучного інтелекту , щоб дізнатися, які реальні можливості та виклики несуть революційні технології, як вони змінять звичну економіку, політику та наше повсякденне життя. З дозволу видавництва Individuum публікуємо уривок із розділу «На захист права володіння персональними даними». 

У Нью-Йорку я зустрівся з Алексом Еліасом, засновником стартапу Qloo. Він ставить перед собою завдання схрестити індивідуальні переваги в різних областях: якщо вам подобається така музика або такий фільм, ви повинні спробувати такий ресторан або піти на побачення з такою людиною … Qloo міг би стати Netflix для життя, що занурює своїх користувачів у тотальні рекомендації, від яких ніщо не вислизає. У цьому сенсі прагнення до «культурної персоналізації» є саме тим, що лякає мене в ШІ. Але Алекс — інтелектуал зі Східного узбережжя, аматор джазу, який розуміється на перевагах імпровізації. Він знає, що похід у магазин платівок відкриває перед ним несподівані перспективи, тоді як Spotify дає лише обмежений досвід. Проаналізувавши пошук у Qloo, він констатує, що більшість американців вказує як улюблену книгу Біблію, а як улюблений фільм — останній блокбастер… Залишені на милість алгоритму, що оптимізує, вони, ймовірно, підуть найтрадиційнішим шляхом. Ось чому Алекс включив у програму Qloo те, що він називає “фактором диверсифікації”. З одного боку, елемент випадковості вводиться в систему рекомендацій, щоб влаштовувати сюрпризи та викликати питання. З іншого боку, користувач контролює деяку кількість критеріїв: це щоб влаштовувати сюрпризи та викликати питання. З іншого боку, користувач контролює деяку кількість критеріїв: це щоб влаштовувати сюрпризи та викликати питання. З іншого боку, користувач контролює деяку кількість критеріїв: це «перша директива» в дії! Щоб пробудити свідомість, що задрімала, потрібна випадковість, а щоб переорієнтувати алгоритм — наявність чітко вираженого вибору.

Алекс визнає, що надана індивіду влада над власними уподобаннями, як не дивно, робить Qloo менш ефективним і тому менш прибутковим. Зрештою, лаконічно зауважує він, «більшість людей хоче розчинитися в масі». Але він хоча пропонує їм альтернативу, втішаючи себе думкою про те, що нові покоління будуть чутливішими до різноманітності. Крім того, потрібно розраховувати на вільний ринок алгоритмів, щоб викликати до життя проекти, більш уважні до вільного володаря, і сподіватися, що вони поступово завоюють користувачів, залучених перспективою звільнення. А поки що кожен може спробувати перехитрити машину. Наприклад, користуючись Google Map, я завжди відключаю геолокацію: отримуючи користь із розрахунку відстаней, я хочу сам визначати вулиці, щоб розуміти, куди їду, а не сліпо слідувати за синьою кулькою. У Deezer я намагаюся вибирати композиції зі списку, критерії якого встановив сам (наприклад, «сонати»), не покладаючись на рекомендації. Я сам наголошую на дні народження своїх (справжніх) друзів у щоденнику і не чекаю, поки мені нагадає про них LinkedIn. А ще я йду з ютуба, коли закінчується відео, яке я вибрав, хоча зробити це непросто — адже так легко і приємно віддаватися потоку образів.

Але всі ці хитрощі смішні, тому що ринок підтасований. Оскільки платформи мають практично вільний доступ до наших персональних даних, все наше цифрове життя ми займаємося тим, що живимо різні ШІ, які у відповідь нами маніпулюють. Nudge усюди, він не залишає жодної можливості для виваженого та самостійного рішення, з’являючись у формі реклами, повідомлень, рекомендацій, повідомлень у соціальних мережах чи спаму у поштових скриньках. Наш мозок постійно піддається хакінгу, який став можливим завдяки тому, що ми скрізь розкидаємо свої дані. Відкрити інтернет — це як опинитися без штанів у публічному місці, подібно до короля з казки Андерсена: він вважав, що на ньому вбрання з дорогоцінної тканини, а насправді було зовсім голим. У цьому може справді переконатися кожен. Браузер, розроблений стартапом myCo, наприклад, дозволяє візуалізувати всіх чужинців, присутніх на сайті, збирає куки та ін. щоб краще інтерпретувати та спрямовувати мою поведінку. Це пограбування абсолютно законне: протягом дня кілька разів ставлячи галочку «Я згоден» у користувацьких угодах, ми добровільно йдемо на те, щоб бути під ковпаком біля платформ. Хитростям, які сайти та програми використовують для того, щоб збирати наші дані, немає числа. Так, програма, що включає ліхтарик на вашому смартфоні, в обмін на цю крихітну послугу забирає всі ваші дані з геолокації. Пам’ятаю, як сидів у ресторані в Парижі навпроти підприємця, який з гордістю оголосив про свій новий «інноваційний проект» — стежити за пересуваннями миші екраном, щоб відточити профіль користувачів. На моє запитання, чи не надто нав’язливий характер носить ця технологія, він знизав плечима і відповів, що вона послужила б хорошу службу всім нам, після чого почав обговорювати зі своїм сусідом по столу проблеми благодійності. Тартюфи 2.0 явно заслуговують на нову п’єсу…

Ну і як тоді «цифрові» громадяни можуть ретельно відбирати алгоритми, якщо кожним своїм кліком, кожним відкриттям програми вони дають ШІ зброю проти себе? Якщо нам не вдасться тримати наші дані при собі, ми потрапимо під владу могутніх ШІ, які унеможливлять будь-який вільний вибір, а отже, і появу будь-яких альтернатив. Європейський загальний регламент захисту даних (GDPR) спробував було ввести свого роду контроль, вимагаючи більшої прозорості від інтернет-компаній. Однак у результаті нам ще частіше доводиться кликати на «Я згоден», але при цьому ми так і не отримали жодної реальної можливості заперечувати умови чи вести переговори.

[…]

Якщо ми хочемо зрозуміти, як влаштовано цифрове пограбування, жертвами якого ми сьогодні стали, достатньо почитати роботи Жоржа Дюбі про економіку Середньовіччя. На початку попереднього тисячоліття кріпаки, прив’язані до землі, віддавали феодалу основну частину плодів своєї праці в обмін на «безкоштовні послуги», більш-менш реальні: захист під час війни або користування сільською інфраструктурою, що належить феодалу (піччю, млином, давильним пресом). Потрібно було дуже багато часу, щоб селяни поступово отримали право передавати майно у спадок, вільно торгувати плодами своєї праці і, нарешті, відчужувати їх за наявності документів про право власності, яке може перепоступатися. Велика французька революція поклала край феодальної ленной системі і розширила декларація про володіння землею: кріпак став вільною людиною. За кожної технічної інновації спалахують схожі дебати. Через кілька століть після винаходи друкарства Бомарше очолив боротьбу письменників за визнання авторських прав. Під час промислової революції винахідники досягли створення справжнього патентного режиму. Людина завжди повинна боротися за право вільно розпоряджатися своєю власністю та плодами своєї творчості, будь вони матеріальними чи інтелектуальними.

Сьогодні ми стали цифровими кріпаками, передавши право на збирання всіх наших даних в обмін на безкоштовні послуги (цінність яких не завжди однозначна), що надаються нашими новими феодалами. Ми постимо по мільярду фотографій у фейсбук щодня. Саме так по мільярду. Після обробки алгоритмами, в які інтегровано розпізнавання осіб, ця скарбниця даних приносить щоквартальний прибуток близько кількох мільярдів доларів. Який відсоток від неї потрапляє до вихідного виробника? Ніякий. Ну хіба що чайові у вигляді мережі друзів… Ми не тільки не можемо вести переговори з нашим феодалом щодо даних, нам заборонено навіть продавати їх на ринку: у травні 2018 року Олі Фрост, заповзятливий британський мілініал, який виставив на eBay свої посади у фейсбуці десять років, був змушений зняти свою пропозицію як таке, що порушує умови користування соціальною мережею. Однак перебіг міркувань Олі Фроста здавався надзвичайно переконливим: «Оскільки всі заробляють гроші на моїх персональних даних, чому б мені не заробляти їх самому?» Ставки на аукціоні сягнули 400 доларів, але феодал явно було змиритися з подібним порушенням його прерогатив. Воно й зрозуміло: за даними Європейської комісії, вартість персональних даних у 2020 році наблизилася до трильйона євро, тобто до 8% ВВП Європейського Союзу. Хто ж захоче ділитися цією небесною манною зі збродом, який її виробляє? але феодал явно було змиритися з подібним порушенням його прерогатив. Воно й зрозуміло: за даними Європейської комісії, вартість персональних даних у 2020 році наблизилася до трильйона євро, тобто до 8% ВВП Європейського Союзу. Хто ж захоче ділитися цією небесною манною зі збродом, який її виробляє? але феодал явно було змиритися з подібним порушенням його прерогатив. Воно й зрозуміло: за даними Європейської комісії, вартість персональних даних у 2020 році наблизилася до трильйона євро, тобто до 8% ВВП Європейського Союзу. Хто ж захоче ділитися цією небесною манною зі збродом, який її виробляє?

Дивно, наскільки пасивно ми приймаємо цей цифровий феодалізм… Ймовірно, кріпаки XII століття навіть не мріяли про те, щоб оскаржувати права сеньйора, що лежить під своїм балдахіном, — фігури так само священної, що й сьогоднішній підприємець із технологічного сектору на сцені TED Talk. Але оскільки все прискорюється, революція може статися швидше. Право власності на персональні дані, що поки що не існує ніде у світі, поклало б край цьому пограбуванню. Воно включало б виробника даних у ланцюжок виробництва вартості в цифровій економіці, дозволяючи йому монетизувати (або не монетизувати) свої дані згідно з умовами договору, які він обере і які посередники від його імені обговорюватимуть із платформами. Питання про винагороду видається мені другорядним. Важливіше інше: згідно із законом повернувши собі власність на свої дані, людина з новою силою відчує, що вона існує, як селянин, який відчуває задоволення від того, що обробляє «свою» землю. Він має право не вирощувати на ній нічого, крім ожини, будяка і терну, або, у разі даних, має право відмовитися від їх надання, не віддаючи їх в обробку ШІ. Кожен зможе вибирати, що він приховує і на який час, що віддає без дублікатів і кому, що продає за якою ціною. Створивши такий простір індивідуального суверенітету, ми знову станемо собою.

[…]

Сучасна ідеологія шерингу, хоч би якою привабливою вона була, маскує масовий грабіж на користь агрегаторів даних. Регулювання — таке, яке передбачає «Регламент захисту даних», — встановило в цьому будинку двері, але не замок: воно лише змушує нас все частіше кликати по «Я згоден». Відтепер усі ці чужі люди запитують у нас: Я зайду на хвилинку, якщо ви не проти? — вже переступивши поріг. Їм важко відмовити. Право власності на дані встановить на двері замки, перевернувши владні відносини і створивши зону спокою, необхідну для розвитку незалежної особистості. Господар наперед складе список гостей, поставивши договірні умови, на яких він готовий повідомляти свої дані. Гоббс в Левіафані питав: хто, лягаючи спати, не зачинятиме двері на ключ? «Однак ніхто з нас не звинувачує людську природу саму собою. Бажання та інші людські пристрасті власними силами є гріхом». Ніхто не ображатиметься на платформи за те, що вони збирають дані, які ми їм даруємо від щирого серця. Але настав час діяти прагматично і поставити замок, якщо ми хочемо зберегтися як індивіди в епоху ШІ.

Зокрема, можна припустити, що дані кожного зібрані в особистому цифровому гаманці. Ми наперед встановимо у смарт-контракті наші власні умови користування, плід особистої деліберації. Як, з ким та в обмін на що ми готові ділитися нашими особистими даними та якими саме? Різні сайти, програми та платформи, зв’язавшись з нами, тут же будуть проінформовані про наші умови. Це вони повинні їх приймати, а не навпаки. Наприклад, я можу вирішити безкоштовно надати дані про своє здоров’я для дослідницьких цілей, але притримати всі дані про геолокацію, хай навіть мені доведеться оплачувати сервіси на кшталт Waze, які їх сьогодні використовують. Щодо даних про мої електронні покупки, я погоджуся передавати їх без особливих проблем, щоб отримувати рекомендації, заточені під мене (і під час справи мати за це грошову компенсацію). Щось на кшталт блокчейну могло б забезпечити можливість відстеження моїх даних, щоб зберігати «пам’ять» про вихідні умови мого договору: подібна процедура вже використовується у музичній індустрії для охорони прав інтелектуальної власності на музичні композиції. Нескінченні фінансові потоки порядку мікросантиму безперервно створюватимуть дебет і кредит у нашому цифровому гаманці, і при цьому обов’язково з’являться посередники для переговорів щодо вартості даних, за зразком товариств з охорони авторських прав. На зміну цифровому феодалізму прийде ціла економіка нескінченно малого ринку, яка сама керуватиметься ШІ. подібна процедура вже використовується у музичній індустрії для охорони прав інтелектуальної власності на музичні композиції. Нескінченні фінансові потоки порядку мікросантиму безперервно створюватимуть дебет і кредит у нашому цифровому гаманці, і при цьому обов’язково з’являться посередники для переговорів щодо вартості даних, за зразком товариств з охорони авторських прав. На зміну цифровому феодалізму прийде ціла економіка нескінченно малого ринку, яка сама керуватиметься ШІ. подібна процедура вже використовується у музичній індустрії для охорони прав інтелектуальної власності на музичні композиції. Нескінченні фінансові потоки порядку мікросантиму безперервно створюватимуть дебет і кредит у нашому цифровому гаманці, і при цьому обов’язково з’являться посередники для переговорів щодо вартості даних, за зразком товариств з охорони авторських прав. На зміну цифровому феодалізму прийде ціла економіка нескінченно малого ринку, яка сама керуватиметься ІІ.

Фінансовий аспект, який передбачається правом власності, ставить закономірні питання. З одного боку, чому ми повинні платити, хай і дуже скромну суму, за послуги, які досі надавалися безкоштовно? Хіба мало заборонити використання особистих даних у комерційних цілях, у логіці «Регламенту про охорону даних»? Але тоді розвалиться вся модель цифрової економіки, позбавивши нас численних послуг, які може надати ШІ. Якщо ми вважаємо, що за виробництво вартості належить винагорода, тоді ця логіка торгового обміну повинна однаково застосовуватися і до індивіда, який виробляє дані, і до платформи-агрегатора. Якщо Waze вибирає для мене найкращий маршрут, але при цьому я відмовляюся від реклами ресторанів, що знаходяться на цьому маршруті, необхідно знайти інші способи відшкодування шкоди платформам. В іншому випадку я стану «безбілетником», який користується чужими даними, нічим не поділяючись натомість. Анонімність має власну ціну.

[…]

На боці ШІ власність на особисті дані призведе до втрати ефективності, з чим доведеться упокоритися всьому суспільству. Будь-кого порадує, що прибутки Facebook постраждають через втрату точності в таргетуванні реклами, проте та сама неоптимальність торкнеться й інших громадських послуг. Можна, звичайно, уявити, що окремі категорії особливо важливих для суспільства даних будуть «націоналізовані», подібно до того, як дозволено вилучати землю у приватних землевласників для будівництва життєво важливих об’єктів інфраструктури. Але така процедура може мати лише надзвичайний характер, і рішення про неї має прийматися судом, який встановить, якою мірою експропріація відображає мотив загального інтересу. В інших випадках доведеться визнати, що розумне місто чи розумні лічильники вже не будуть настільки розумними, а безпілотні автомобілі настільки безпілотними. Допустивши можливість індивідуального відхилення, право власності на дані викличе аварії, вуглецеві викиди та розриви відносин. Воно завадить суспільству стати ідеальним, повернувши цим можливість еволюції… як регресії. Терпіти подібні випадковості, безпосередні та конкретні, заради розпливчастої обіцянки прогресу — тяжкий тягар для законодавців. Але саме на цій лінії розлому відбувається боротьба міжnudge і автономією, щасливим рабством та поверненням епохи освіти.

Вже зараз є чимало стартапів, які б хотіли взяти участь у цій війні за повернення людині автономії. Такі екосистеми особливо динамічно розвиваються у Франції — країні, яка вигадала авторське право. У Парижі, Ліоні, Монпельє підприємці прагнуть вдихнути життя в це право власності, в якому регулятор поки що відмовляє. Одні розробляють data wallet: використовуючи передбачене GDPR право на мобільність, він дозволяє кожному отримувати свої особисті дані та контролювати їх надання. Інші шукають спосіб надати персональним даним грошову вартість, користуючись правовими «сірими зонами». Треті організують «цифрові кооперативи», які спільно використовують дані своїх членів і виплачують їм дивіденди. Як це вже неодноразово бувало, технологія змусить право підлаштовуватися під себе. Можливо, об’їхавши всю планету, в результаті я знайду рішення зовсім близько від будинку, як капітан Хеддок, який знайшов скарби Лікорну у Муленсарі?

Джерело

Rematch – Redux без шаблонного коду

rematch У цій статті я пропоную вам ознайомитись з основними перевагами Rematch та покажу, як просто та ефективно використовувати його для керування станом ваших додатків. Думаю, що, незалежно від вашого досвіду у розробці, ви знайдете корисну інформацію, яка допоможе вам у повсякденній роботі.

Rematch пропонує зручний синтаксис та дозволяє уникнути написання численних бойлерплейтів, властивих Redux. За допомогою нього ви можете швидко створювати та організовувати Redux-сховище, заощаджуючи при цьому час та спрощуючи процес розробки. Rematch ідеально підходить для різних типів проектів, незалежно від їх розміру та складності.

За допомогою Rematch ви можете визначити моделі (models) та їх стан, ефекти (effects) та ред’юсери (reducers). Він пропонує зручні інструменти для роботи з асинхронними операціями, а також можливість створення селекторів (selectors) для вибірки даних зі сховища. Все це допомагає організувати логіку вашої програми та керувати її станом з мінімальними зусиллями.

Насамперед, я б рекомендував цю бібліотеку тим, хто тільки починає вивчати управління станом додатків або пише новий проект з нуля, тому що Rematch набагато простіше в освоєнні, ніж багато інших бібліотек, забезпечує набагато приємніший інтерфейс і, до того ж, набагато легше.

Введення в Rematch

Rematch – це інтуїтивний та легковажний фреймворк для керування станом вашої веб-додатки на основі Redux. Він пропонує простий та ефективний підхід до організації та управління Redux-сховищем, скорочуючи кількість зайвого коду та спрощуючи розробку.

Rematch — це інструмент для управління станом, а й філософія розробки. Він ставить собі завдання зробити код більш зрозумілим, легко підтримуваним і масштабованим. Однією з ключових особливостей Rematch є його інтеграція з Redux без необхідності писати численні бойлерплейт-коди та налаштування. Це дозволяє розробникам швидше почати ним користуватися та зосередитися на бізнес-логіці своєї програми.

У процесі роботи з бібліотекою я наголосив на наступних її корисних рисах:

  • невеликий обсяг (менше 2Кб);
  • відсутність необхідності зміни;
  • вбудована підтримка сайд-ефектів;
  • підтримка Redux Devtools, TypeScript;
  • підтримка динамічного додавання ред’юсерів, гарячого перезавантаження;
  • можливість створення кількох сховищ;
  • розширюваність функціоналу за допомогою плагінів;
  • можливість використання у React Native.

Якщо порівнювати Rematch із прямим використанням Redux, його основні переваги:

  • Простота та зрозумілий синтаксис . Rematch пропонує просту та інтуїтивно зрозумілу модель, яка дозволяє визначити моделі стану, ефекти та редьюсери з мінімальною кількістю коду. Він звільняє розробників від необхідності писати численні дії (actions), константи та редьюсери, що робить код лаконічнішим і зрозумілішим.
  • Модульність і перевикористовуваність . Rematch дозволяє організовувати код вашої програми у модулі (моделі), що сприяє логічному поділу функціональності та повторному використанню коду. Ви можете створювати незалежні моделі, що містять свій стан, ред’юсери та ефекти, та комбінувати їх в одне сховище.
  • Потужні ефекти . Rematch надає простий спосіб визначення та обробки асинхронних операцій та сайд-ефектів за допомогою ефектів. Він інтегрується з middleware Redux, що дозволяє легко керувати асинхронними запитами, зверненнями до API та іншими ефектами сайду.
  • Розширюваність . Rematch дозволяє легко розширювати функціональність за допомогою плагінів. Ви можете використовувати готові плагіни або створити свої власні, щоб додати нові можливості та інтеграції у свою програму.

Незважаючи на представлені плюси, важливо розуміти, що Rematch – це лише обгортка над Redux, яка надає розробникам спрощене API. Проблеми продуктивності та додаткового функціоналу, на жаль, не вирішить перехід від використання Redux до Rematch.

Основні концепції Rematch

Тут я покажу основні концепції Rematch, які допоможуть вам організувати та керувати станом вашої програми.

Моделі (Models)

Моделі є ключовим поняттям у Rematch. Вони є незалежними блоками коду, що містять стан, ред’юсери та ефекти. Кожна модель має унікальне ім’я і може бути поєднана з іншими моделями в одне сховище (store).

Давайте створимо просту модель “counter”, яка міститиме стан лічильника та редьюсери для його збільшення та зменшення, а також ефект для асинхронного збільшення:

/* ./models/count.ts */

import { createModel } from "@rematch/core";
import { RootModel } from ".";

export const count = createModel<RootModel>()({
  state: 0, // початковий стан
  reducers: {
    increment(state, payload: number) {
      return state + payload;
    },
    decrement(state, payload: number) {
      return state - payload;
    },
  },
  effects: (dispatch) => ({
    async incrementAsync(payload: number, state) {
      await new Promise((resolve) => setTimeout(resolve, 1000));
      dispatch.count.increment(payload);
    },
});

export default counter;
import { Models } from "@rematch/core";
import { count } from "./count";
import { example } from "./example";

export interface RootModel extends Models<RootModel> {
  count: typeof count;
  example: typeof example;
}

export const models: RootModel = { count, example };

Сховище (Store)

Сховище є об’єднання декількох моделей в одну структуру даних. Воно є єдиним джерелом правди для вашої програми та зберігає стан усіх моделей.

Для створення сховища Rematch використовує функцію init, яка приймає об’єкт із моделями:

import { init, RematchDispatch, RematchRootState } from "@rematch/core";
import { models, RootModel } from "./models";

export const store = init({
  models,
});

export type Store = typeof store;
export type Dispatch = RematchDispatch<RootModel>;
export type RootState = RematchRootState<RootModel>;

Диспатч екшенів (Dispatch actions)

За допомогою диспатча ви можете викликати редьюсери та ефекти ваших моделей. Диспатч можна викликати звичним способом, як у Redux, або коротким записом:

const { dispatch } = store;

dispatch({ type: "count/increment", payload: 1 });
dispatch.count.increment(1);

dispatch({ type: "count/incrementAsync", payload: 1 });
dispatch.count.incrementAsync(1);

Доступ до стану та дій

Щоб отримати доступ до стану та дій моделей у компонентах вашої програми, ви можете використовувати хук useSelector та метод dispatch:

import * as React from "react";
import { useSelector, useDispatch } from 'react-redux';
import { RootState, Dispatch } from './store';

const CounterComponent = () => {
  const count = useSelector((state: RootState) => state.count);
  const dispatch = useDispatch<Dispatch>();

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => dispatch.count.increment(1))}>Increment</button>
      <button onClick={() => dispatch.count.decrement(1))}>Decrement</button>
    </div>
  );
};

export default CounterComponent;

У цьому прикладі ми використовуємо хук useSelector для отримання значення стану “counter” зі сховища, а також метод dispatch для виклику відповідних редьюсерів.

Ефекти (Effects)

У Rematch ефекти надають можливість виконувати асинхронні операції, такі як запити до сервера чи обробка даних. Вони дозволяють вам легко керувати побічними ефектами у вашому додатку.

Для додавання ефектів у модель можна використовувати ключове слово effects і визначити потрібні методи. Давайте додамо ефект для завантаження даних із сервера:

const counter = createModel<RootModel>()({
  state: 0,
  reducers: {
    // ...
  },
  effects: {
    async fetchData(payload, rootState) {
      try {
        const response = await fetch(`http://example.com/${payload}`);
        const data = await response.json();
        // Дії з отриманими данными
      } catch (error) {
        // Обробка помилок
      }
    },
  },
});

Плагіни (Plugins)

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

Існує безліч плагінів, доступних для використання з Rematch. Деякі з них були написані безпосередньо командою авторів цієї бібліотеки, але є такі, які створювалися спільнотою, на npm/github, і ніхто не забороняє написати свій власний плагін.

Нижче ви знайдете короткий опис плагінів, створених командою Rematch:

  • @rematch/immer : Обертає ваші ред’юсери за допомогою бібліотеки immer, що дозволяє безпечно виконувати зміни стану за допомогою мутацій, зберігаючи при цьому незмінність стану.
  • @rematch/select : Додає можливість використовувати селектори (selectors) для вибору даних стану. Селектори створюються за допомогою бібліотеки reselect за замовчуванням і автоматично зв’язуються із залежностями селекторів з інших моделей.
  • @rematch/loading : Плагін для відображення індикаторів завантаження під час виконання ефектів. Допомагає уникнути ручного керування станом завантаження, додаючи автоматичну підтримку завантаження Rematch.
  • @rematch/updated : Плагін, що дозволяє підтримувати тимчасові мітки (timestamps) під час виклику ефектів. Він застосовується переважно для оптимізації ефектів і може використовуватися для запобігання виконання дорогих запитів протягом певного періоду часу або обмеження частоти виконання ефектів.
  • @rematch/persist : Надає автоматичну персистентність стану Redux. Це дозволяє зберігати стан між перезавантаженнями сторінки або після закриття та повторного відкриття програми.
  • @rematch/typed-state : призначений для перевірки типів стану під час виконання. Використовує proptypes для опису очікуваної форми типів.

Підсумок

У цій статті я розповів про Rematch – потужну бібліотеку для управління станом додатків. На підставі свого досвіду виділив основні переваги Rematch і на практиці показав, як легко та ефективно використовувати його для організації Redux-сховища та управління станом.

Rematch пропонує простий та зручний синтаксис, який значно зменшує кількість бойлерплейту, пов’язаного з Redux, і я думаю, що це може зробити його привабливим інструментом для простих розробників, які хочуть зосередитися на розробці програми, а не на складностях управління станом.

Завдяки підтримці безлічі плагінів, Rematch може бути легко розширений та адаптований під ваші конкретні потреби. Ми розглянули деякі популярні плагіни, такі як плагін для збереження стану за допомогою redux-persist, плагін для використання immer.js та плагін для створення селекторів за допомогою reselect.

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

Сподіваюся, що ця стаття допомогла вам зрозуміти переваги та можливості Rematch та надихнула вас використовувати його у ваших проектах. Бібліотека пропонує простоту, компактність та масштабованість, роблячи процес розробки більш ефективним та приємним.

Джерело

Відмінності базових елементів блокчейн: чому ця технологія не здатна здійснити революцію

blockchain

Такі слова, як Біткоїн та блокчейн іноді використовуються як взаємозамінні, але насправді між ними є деякі непорозуміння. Такої думки дотримується Кевін Вербах, професор юридичних наук та ділової етики Уортонської школи бізнесу. Він заявив про те, що в технології блокчейн базові елементи мають певні відмінності, при цьому йдеться про криптовалюту, криптоаналіз і саму блокову систему.

У наші дні важко не помітити ажіотажу навколо заяв про те, як криптовалюти та технологія blockchain можуть змінити все (або принаймні зробити багато). Тим не менш, такий галас навколо цього є ризиком масштабної афери, марною і небезпечною. Дивно, що така величезна аудиторія досі не розуміє, що відбувається. Однією з головних причин плутанини є те, що всі говорять про різні речі.

Представники угруповань усіх трьох складових блокчейну поділяють базовий набір принципів проектування та технологічних основ, але в цілому їх цілі та перспективи практично повністю відрізняються один від одного. Спробуймо розібратися в ситуації.

Існує криптовалюта — ідея, що мережі можуть безпечно передавати цифрові активи без центральних точок контролю. Існує блок-ланцюжок — ідея про те, що мережі можуть колективно досягати консенсусу щодо інформації через встановлені межі довіри. І є криптоаналіз — ідея про те, що віртуальні валюти можуть відігравати роль активів, що торгуються.

Перше – воістину революційне поняття, але це ще не знає, що революція буде успішною. Друге та третє — інновації, що змінюють гру та здатні до суттєвого еволюціонування.

Криптовалюти

Цифрові гроші — це те, про що багато хто, напевно, чув найбільше, починаючи з Bitcoin. Найпростіший спосіб зрозуміти їх – не вивчати деталі майнінгу чи внутрішнього пристрою. Натомість краще зосередитися на децентралізації довіри.

Багато дій потребують довіри. Без довіри проект закону вартістю кілька мільйонів доларів — це лише аркуш паперу, а голосування на виборах — безглуздий ритуал. Традиційно під словом «довіра» малася на увазі залежність від партнерів, установ чи посередників. Ці централізовані архітектури довіри є сильними. Серед іншого, вони принесли нам сучасну індустріальну цивілізацію. Але є й недолік у довірі. Він має на увазі вразливість. Люди, уряди та компанії, яким ми довіряємо, можуть виявитися ненадійними з низки причин. Біткоїн показав, що можна створити щось цінне (наприклад, гроші), яким можна довіряти без необхідності довіряючи конкретній особі чи організації, зокрема щодо перевірки транзакцій.

Ця ідея, якщо вона буде повністю реалізована (у разі «якщо» несе у собі крані важливий сенс), може змінити суспільство. Світ може отримати прозорі компанії, які справді відображають волю зацікавлених у їхньому розвитку сторін, уряди, які справді відображають волю своїх громадян, та інтернет, звільнений від цензури та впливу ззовні. Це здатне покласти край фейковим новинам та масової автоматизації повсякденного життя для покращення рівня життя людства. Або принаймні люди можуть отримати рішення, які помітно покращать їхній статус-кво. Децентралізація несе у собі цінність у вигляді незалежно від способу.

Витрати

Цей термін застосовується практично до всіх сфер діяльності. Для Біткоіна витрати пов’язані з дуже повільною мережею з обмеженою функціональністю, яка витрачає величезну кількість електроенергії та збагачує співтовариство майнерів. Можливо, це того варте. Однак, можливо, у майбутньому технологічні рішення завдяки удосконаленню блокової технології призведуть до зниження витрат. Поки що ми цього не знаємо. Так, ринкова капіталізація Біткоїна становить мільярди доларів, але це насправді просто криптоаналітичне мислення. Хтось використовує його, щоб робити щось, окрім прагнення розбагатіти шляхом торгівлі чи уникнути уваги з боку правоохоронних органів? Він стає все гіршим і гіршим, тому що на ринку з’являються все нові і нові криптовалюти. Деякі з них не поступаються,

Прибуток

Що справедливо для невеликих груп, обмежених програм та «особливих» користувачів, не обов’язково може стати мейнстримом. Доки не з’явився Facebook, було неясно, хто і як може заробляти реальні гроші на соціальних мережах, які спочатку були місцем простого спілкування підлітків і молоді. Однак сам факт появи Facebook не доводить, що це було неминучим.

Деякі зі ставок на криптовалютну революцію можна довести правильно. Такі ставки мають широкий потенціал зростання, але насправді це все ж таки просто гра. Насправді реальні революції відбуваються не так часто. І коли це трапляється, то відбувається із серйозними побічними збитками.

Blockchain

Блокчейн має спільне коріння з криптовалютами, але його мета зовсім інша. Замість спроби уникнути довіри блокчейн прагне надмірно обмежити довіру. Ми справді довіряємо собі чи своїй власній організації. Але жодна людина чи компанія не є островом в океані. Але навіть острівні держави змушені взаємодіяти з іншими учасниками світу через воду.

Світ наповнений процесами, особливо серед великих компаній та урядів, де їх слід відстежувати з однієї довіреної зони до іншої. Фірми щорічно витрачають близько $10 трлн на «логістику», що скорочує витрати на транспортування через системи, контрольовані кимось. Виробники, дистриб’ютори та роздрібні торговці зберігають власні надійні (але незалежні) записи даних про потоки через ланцюжки поставок.

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

Значна частина транзакційних витрат між фірмами (іноді і всередині них) випливає з обмеженої еластичності довіри. Якщо кожна сторона транзакції довіряє залученій інформації, навіть якщо вони не довіряють одна одній, витрати можуть знизитися, а продуктивність різко змінитись. У цьому полягає суть бачення блочної ланцюжка.

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

Криптоаналіз

Під цим терміном розуміється аналіз цифрових токенів, які перетворюються на інструменти торгівлі. Потенційна шкала цього напряму величезна. Обсяги ринків досягають трильйони доларів, що не можна назвати незвичайним у сучасній фінансовій сфері. При цьому вони розглядаються не як спосіб полегшення дій без централізованої довіри, а як новий клас інвестиційних активів. Оскільки вони є цифровими, вони можуть теоретично торгуватися більш ефективно, ніж існуючі інструменти. За своєю суттю вони гнучкіші і глобальніші. Практично всі основні гравці Уолл-стріт прагнуть взяти участь у різних стартапах у ролі інституційних інвесторів.

Як тільки буде встановлено фундаментальне значення цифрових токенів у децентралізованій мережі, чому б просто не використовувати їх для заробляння грошей? У будь-якому випадку вони залежатимуть від криптовалюту, бо торгівля можлива лише чимось справді цінним. Однак вони також мають бути безпечними. Але криптоаналіз ігнорує чи відкидає ідею криптовалют про те, що довіра — «майже непристойність» (вирізка з цитати експерта, який робив початковий аудит безпеки Біткоіна ). Для трейдера криптоаналіз довіри та відсутність довіри — це не що інше, як засіб для досягнення мети, що називається ліквідністю.

Інший спосіб подумати про це полягає в тому, що криптоаналіз передбачає свого роду злиття функцій токенів та криптовалют. Якщо ви хочете використовувати Біткоїн для оплати покупок, Ефіріум для придбання обчислювальних циклів для розподілених додатків, Filecoin для покупки хмарного сховища файлів або Augur Rep для перевірки результатів ринків прогнозів, ви забезпечуєте цінність цих токенів на основі того, що ви отримуєте за рахунок їх використання.

Теоретично велика потреба у використанні додатків означає скорочення пропозицій і зростання попиту, що штовхає курс вгору. На практиці поки що жодна з додатків не має істотної важливості, тому ціна токенів залишається дуже спекулятивною. Спекуляцію не обов’язково завжди розглядати як щось погане. Це апетит до ризику, який керує фінансовими ринками. Однак іноді спекуляція стимулює ринки рости надто високо. Ключовим питанням для криптовалют є питання про те, чи можна буде контролювати такі умоглядні інстинкти.

У процесі розвитку ринків можуть виникати різні цікаві можливості для «токенізації» фізичних предметів, таких як товари, нерухомість, цифрові речі, такі як інтелектуальна власність та інші види прав, з використанням інструментів фінансової інженерії та аналітики. У цьому напрямі Уолл-Стріт розвивається вже багато років, створюючи необхідні основи.

Не варто перетинати межу

Історії не є взаємовиключними як такі. Успіх чи невдача якогось бачення не обов’язково означають, що аналогічне може статися з іншим. Криптовалюти мають найруйнівніший потенціал, бо вони обіцяють децентралізувати владу. Це також створює великі перешкоди для їхнього успіху.

Як блокові системи, і цифрові валюти зменшують децентралізацію серед інших своїх переваг. Вони різняться в цілях і можуть мати значні можливості, але їх потрібно оцінювати зі свого погляду. Наприклад, чи варто розглядати ICO як нову форму краудфандингу чи спосіб започаткувати децентралізовану економіку? Те, що вважається успішним чи невдалим, виглядає по-різному залежно від відповіді.

Будь-які судження про успіхи або невдачі, пов’язані з блоковою технологією, повинні бути сформульовані з точки зору відповідної підкатегорії. Коли спостерігачі вказують на прийняття блокчейна тією чи іншою компанією або високі ціни на криптовалюти як доказ їхньої життєздатності, вони перетинають межу. Той факт, що у світі ICO існує масове шахрайство та крадіжки, ніяк не належить до низки урядових ініціатив у сфері блокчейну.

Незалежно від того, чи йдеться про бізнес-модель, яка передбачає впровадження блокового ланцюжка в банки, цього недостатньо, щоб говорити про перспективи децентралізованих автоматизованих організацій загалом.

Чим раніше ми перестанемо розглядати блокчейн і криптовалюти як унітарне явище, тим точніше і якісніше ми зможемо оцінити події, що відбуваються.

Джерело

Співвідношення ризику та прибутку при торгівлі криптовалютою та чому це важливо

Співвідношення ризику та прибутку

  • Співвідношення ризику та прибутку – коефіцієнт, призначений для розрахунку потенційної прибутковості від торгової угоди щодо закладеного ризику та з урахуванням стратегії трейдера. RR дозволить зрозуміти, чи є угода вигідною чи ні.
  • Співвідношення ризику та прибутку розраховується після складання торгового плану, визначення точок входу та виходу та рівня встановлення стоп-лосса.
  • Співвідношення ризику та прибутку розраховується індивідуально для кожної позиції на підставі торгової стратегії трейдера з урахуванням статистики та можливостей.
  • Грамотне співвідношення ризику та прибутку дозволяє заробляти у довгостроковій перспективі при правильному аналізі результатів своєї торгової стратегії.

Що таке співвідношення ризику та прибутку (RR)

Співвідношення ризику та прибутку (Risk/Reward Ratio або RR) – коефіцієнт, що показує співвідношення ризику до потенційного прибутку. Конкретне значення RR розраховується перед покупкою активу та дозволяє оцінити потенціал угоди з погляду торгової стратегії трейдера.

Якщо співвідношення ризику до прибутку більше значення 1, то ризик більший, ніж потенційний прибуток. Коли значення нижче одиниці, то потенційний прибуток більший за закладені ризики.

З погляду трейдингу та інвестицій ризик означає потенційний збиток, який трейдер готовий піти, відкриваючи позицію. Рівень ризику зазвичай контролюють за допомогою виставляння ордерів стоп-лосс (Stop Loss), тобто заявок на автоматичний продаж активу при досягненні певної ціни. Це важливий торговий інструмент, і необхідний як обмеження збитків. Рівень ризику є невід’ємною частиною у процесі обчислення потенційного прибутку трейдера та його торгової стратегії загалом.

Прибуток — це різниця між ціною купівлі активу та ціною, за якою його буде продано. У контексті співвідношення RR прибутком є ​​потенційний рівень, який визначає трейдер перед входом до позиції для оцінки потенціалу торгової операції.

Як правильно розрахувати співвідношення ризику та прибутку

Загальноприйнятий варіант розрахунку коефіцієнта RR визначається як відношення ризику до прибутку, тобто RR дорівнює ризик поділений на прибуток. Хоча деякі трейдери через особисті переваги можуть використовувати зворотний варіант, де прибуток ділять на ризик, ми розглянемо стандартний приклад розрахунку.

Припустимо, що бажаєте придбати актив за ціною $100. Ви також вирішили обмежити ризик, тобто поставити стоп-лосс, на позначці $90, і встановили собі цільову ціну, за якою продасте актив, на позначку $130. У цьому випадку співвідношення RR складе 1 до 3 або коефіцієнт з приблизним значенням 0,33. Тобто ризик менший, ніж потенційний прибуток.

У прикладі з тією ж ціною входу ($100), і такою ж цільовою ціною ($130), але з виставленим стоп-лоссом на відмітці $40, співвідношення RR буде рівним 2. Таке значення коефіцієнта вказує, що ризик значно перевищує очікуваний прибуток.

Оптимальне співвідношення ризику та прибутку

Одним із найпопулярніших значень при розрахунках співвідношення ризику та прибутку є 1 до 3 або коефіцієнт 0,33. Також часто використовуються співвідношення 1 до 7, 1 до 10, 1 до 15.

Однак вибір загальноприйнятих варіантів RR є грубою помилкою у трейдингу. Трейдер повинен сам визначити, яке співвідношення RR найкраще підходить під його торгову стратегію на основі досвідчених даних, статистики, кон’юнктури ринку.

Наприклад, якщо трейдер робить тільки 50% успішних угод, то RR рівний 0,5 або 1 до 2 не принесе користі. Цільова ціна продажу активу перед входом до угоди має статистично приносити трейдеру прибуток, а не тільки саме в цій торговій операції.

У прикладі із співвідношенням 1 до 3 або коефіцієнтом 0,33, сенс RR закладено в тому, щоб одна прибуткова угода могла покрити 3 збиткові угоди. У разі коли співвідношення 1 до 5, то одна прибуткова угода повинна буде покрити 5 збиткових.

Перш ніж оцінювати ризики та проводити розрахунки RR, трейдер оцінює потенціал руху ціни, знаходить точку для входу і становить прогноз руху ціни на актив, також визначивши собі момент виходу з позиції.

Тільки після цього є сенс проводити розрахунки RR. Якщо отриманий коефіцієнт відповідає торговій стратегії трейдера, він входить у позицію.

Навіщо розраховувати співвідношення ризику та прибутку

RR розраховується для того, щоб трейдер міг ефективно використовувати свою торгову стратегію, регулюючи потрібний рівень ризику та прибутку для отримання прибутку на довгому відрізку часу.

Навіть якщо відсоток успішних операцій невеликий, скажімо 20%, грамотне співвідношення ризику і прибутку може принести трейдеру дохід на довгій дистанції.

Відповіді на популярні запитання

Що таке співвідношення прибутку до ризику?

Risk/Reward Ratio або RR – це спосіб оцінити потенціал угоди для трейдера на основі його торгової стратегії та можливостей. Простіше кажучи, RR показує, чи є угода вигідною чи ні на основі потенційних ризиків та прибутку.

Що таке вінрейт у трейдингу?

Вінрейт у трейдингу – ставлення кількості прибуткових угод до збиткових. Наприклад, якщо ви закриваєте 60% угод із прибутком, а 40% — зі збитком, то ваш вінрейт становитиме 0,6 до 0,4, або 1,5.

Що таке 1 до 3 у трейдингу?

1 до 3 у трейдингу – одне з найпопулярніших співвідношень прибутку до ризику у трейдерів. Воно означає, що як мінімум 1 із 4 угод має бути прибутковою. Проте таке співвідношення має відповідати обраній торговій стратегії та співвідноситися з вінрейтом. У деяких випадках оптимальне співвідношення прибуткових угод до збиткових може відрізнятися.

Що таке RR у трейдингу?

RR у трейдингу – абревіатура від англійського терміна Risk/Reward (Ризик/дохідність) і означає співвідношення прибутку до ризику.

Джерело

react-query vs SWR і чи позбудемося ми Redux?

react-query vs SWR

Давайте спочатку познайомимося з обома бібліотеками, щоб порівняти їх і переконатися, що краще. react-query та SWR – це дві популярні бібліотеки для керування станом даних у React-додатках. Вони обидві призначені для полегшення роботи з даними, що отримуються з сервера, але мають деякі відмінності у функціональності та підходах.

react-query

react-query  — це потужна бібліотека для керування станом даних у програмах React. Вона надає прості та ефективні інструменти для роботи з даними, що отримуються з сервера, та спрощує взаємодію з API-запитами. Нижче познайомимося з базовими фічами цієї бібліотеки:

Кешування даних:  Однією з основних можливостей react-query є кешування даних. Під час виконання запитів до сервера бібліотека автоматично кешує отримані дані. Це дозволяє уникнути повторних запитів на сервер при зверненні до тих самих даних у різних частинах програми. Кеш react-query також автоматично інвалідується під час оновлення даних на сервері або вручну за допомогою інвалідності.


queryClient.invalidateQueries()

Автоматичне оновлення даних:  react-query надає механізм автоматичного оновлення даних на основі певних подій. Коли запит на сервер виконується, бібліотека автоматично оновлює кешовані дані при успішному завершенні запиту, мутації або ручних викликах оновлення. Це дозволяє завжди мати актуальні дані у додатку.

Повторні запити:  У разі помилок під час виконання запиту react-query надає механізм повторних запитів, який автоматично спробує повторити запит. Це особливо корисно під час роботи з нестабільним інтернет-з’єднанням або тимчасовими проблемами на стороні сервера. Також можна налаштувати інтервал між повторними запитами або вказати максимальну кількість повторів у налаштуваннях.

Оптимістичні оновлення:  React-query підтримує оптимістичні оновлення даних. Це дозволяє оновлювати кешовані дані негайно ще до того, як запит на сервер буде завершено. Це створює плавне враження для користувачів та підвищує чуйність інтерфейсу.

Обробка помилок та повторні запити:  Бібліотека спрощує обробку помилок під час виконання запитів. Вона надає механізм повторних запитів під час помилок, що дозволяє автоматично спробувати повторити запит для отримання даних. Це особливо корисно під час роботи з нестабільним інтернет-з’єднанням або тимчасовими проблемами на стороні сервера.

Гнучка настройка запитів:  Бібліотека надає гнучкі засоби для налаштування запитів, дозволяючи контролювати кешування, час життя даних, політики повторних запитів та багато іншого.

// 
import { QueryClient } from '@tanstack/react-query';

const queryClient = new QueryClient({
   defaultOptions: {
      queries: {
      staleTime: Infinity,
    },
  },
});

// 
queryClient.setQueryDefaults(
  ['todos'], { staleTime: 3000 }
)

Мутації:  react-query підтримує мутації, що дозволяє легко виконувати зміни даних на сервері. Мутації є особливими функціями, які виконують запити на сервер для зміни даних, і автоматично оновлюють відповідний кеш після успішного завершення мутації.

Інтеграція з React:  react-query розроблена спеціально для роботи з React та інтегрується з ним без проблем. Вона надає хуки та компоненти, які дозволяють зручно взаємодіяти з даними та відображати їх в інтерфейсі React-компонентів.

//
import {
  QueryClient,
  QueryClientProvider,
  useQuery,
} from '@tanstack/react-query'

const queryClient = new QueryClient()

export default function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <Example />
    </QueryClientProvider>
  )
}

function Example() {
  const { isLoading, error, data } = useQuery({
    queryKey: ['repoData'],
    queryFn: () =>
      fetch('https://api.github.com/repos/TanStack/query').then(
        (res) => res.json(),
      ),
  })

  if (isLoading) return 'Loading...'

  if (error) return 'An error has occurred: ' + error.message

  return (
    <div>
      <h1>{data.name}</h1>
      <p>{data.description}</p>
      <strong>👀 {data.subscribers_count}</strong>{' '}
      <strong>✨ {data.stargazers_count}</strong>{' '}
      <strong>🍴 {data.forks_count}</strong>
    </div>
  )
}
//
import {
  useQuery,
  useMutation,
  useQueryClient,
  QueryClient,
  QueryClientProvider,
} from '@tanstack/react-query'
import { getTodos, postTodo } from '../my-api'

const queryClient = new QueryClient()

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <Todos />
    </QueryClientProvider>
  )
}

function Todos() {
  const queryClient = useQueryClient()

  const query = useQuery({ queryKey: ['todos'], queryFn: getTodos })

  //
  const mutation = useMutation({
    mutationFn: postTodo,
    onSuccess: () => {
      // 
      queryClient.invalidateQueries({ queryKey: ['todos'] })
    },
  })

  return (
    <div>
      <ul>
        {query.data?.map((todo) => (
          <li key={todo.id}>{todo.title}</li>
        ))}
      </ul>

      <button
        onClick={() => {
          mutation.mutate({
            id: Date.now(),
            title: 'Do Laundry',
          })
        }}
      >
        Add Todo
      </button>
    </div>
  )
}

render(<App />, document.getElementById('root'))

SWR

SWR  (Stale-While-Revalidate) – це бібліотека для керування станом даних у React-додатках, яка орієнтована на кешування даних та автоматичну валідацію. Вона надає простий та ефективний спосіб роботи з даними, що отримуються з сервера, та спрощує оновлення даних в інтерфейсі програми. Нижче познайомимося з базовими фічами цієї бібліотеки:

Кешування даних:  Однією з основних можливостей SWR є кешування даних, які отримують із сервера. Коли запит на сервер, бібліотека автоматично кешує отримані дані. Це дозволяє уникнути повторних запитів на сервер при зверненні до тих самих даних у різних частинах програми.

Автоматична валідація даних:  SWR використовує стратегію Stale-While-Revalidate, що означає, що вона повертає старі дані з кешу (якщо вони доступні) і одночасно запускає повторний запит на сервер для отримання актуальних даних. Таким чином користувач завжди бачить старі дані, навіть якщо сервер вже повернув нові. Це дозволяє надати користувачеві миттєвий відгук та актуальні дані після завершення повторного запиту.

Повторні запити:  У разі помилок під час виконання запиту SWR надає механізм повторних запитів, який автоматично спробує повторити запит. Це особливо корисно під час роботи з нестабільним інтернет-з’єднанням або тимчасовими проблемами на стороні сервера.

Оптимістичні оновлення:  SWR також підтримує оптимістичні оновлення даних. Це означає, що при оновленні даних на сервері вона моментально оновлює дані в кеші і показує користувачеві оновлені дані без очікування відповіді від сервера. Після успішного завершення запиту на сервер кеш автоматично оновлюється з актуальними даними.

Політики повторних запитів:  SWR дозволяє гнучко налаштовувати повторні запити за допомогою різних політик. Наприклад, можна встановити інтервал між повторними запитами або вказати максимальну кількість повторів.

Інтеграція з React:  SWR розроблена для інтеграції з React і надає хук  useSWR(), який дозволяє зручно взаємодіяти з даними та обробляти стан запитів.

const fetcher = (...args) => fetch(...args).then(res => res.json());

//
import useSWR from 'swr'
 
function Profile () {
  const { data, error, isLoading } = useSWR('/api/user/123', fetcher)
 
  if (error) return <div>Помилка загрузки</div>
  if (isLoading) return <div>загрузка...</div>
 
  //
  return <div>привіт, {data.name}!</div>
}

Тепер, давайте порівнювати ці дві бібліотеки та визначити, який краще вибрати:

Як ми зрозуміли, основний акцент бібліотеки  react-query  робиться на керування даними на клієнтській стороні програми, особливо на роботу з даними, що отримуються з сервера (API-запити). react-query пропонує потужні засоби для кешування даних, автоматичної інвалідності кешу та обробки запитів.

SWR  в першу чергу орієнтована на кешування та автоматичну валідацію даних, які отримують з сервера. Вона надає зручні засоби для кешування та повторних запитів даних із підтримкою стратегії “Stale-While-Revalidate” (збереження старих даних, поки виконується повторний запит).

Також  react-query  надає гнучкі засоби для налаштування запитів та підтримує мутації, дозволяючи легко виконувати зміни даних на сервері, для цього  react-query  надає хуки ( useMutation(),  useQuery()) та компоненти для зручної взаємодії із запитами та мутаціями. SWR  у свою чергу  надає лише один хук —  useSWR, який дозволяє виконувати запити та автоматично кешувати дані, а також обробляти їх оновлення та помилки.

Підсумок

Загалом, якщо ваш проект величезний і має багато  GET/POST запитів, краще використовувати react-query, якщо проект не великий і не має багато запитів, то краще вибирати SWR. Все залежить від обсягу та функціональності проекту.

Тепер відповімо на найголовніше запитання: «Чи позбавимося ми Redux?». Насправді повністю позбутися Redux не вийде, але певною мірою, так, позбудемося. Як? У всіх запитах, де використовується react-query/SWR використовувати Redux не потрібно, ці бібліотеки самі всі дані беруть з бекенда і працюють як “state manager”, а також плюс до цього кешують дані, що це навіть дуже вигідно.

Джерело

Абстрактні типи даних для початківців

Абстрактні типи данних

“У комп’ютерній науці всі проблеми можуть бути вирішені за допомогою додаткового рівня непрямості,” – Девід Вілер

Типи даних – це природно

Людина є істотою з дуже розвиненим образним мисленням. Саме наша здатність до створення абстракцій та узагальнення прожитого досвіду стала ключем до розвитку цивілізації. Ми користуємося цими здібностями від народження, навіть не замислюючись. Наприклад, ми з дитинства працюємо з різними типами даних, діючи швидше інтуїтивно, не даючи їм формального опису. Ми знаємо, що числа можна складати і множити, а зі слів складати речення. Навряд чи нам спаде на думку спробувати перемножувати слова. Таким чином, ми розуміємо, коли бачимо написані символи (літери, наприклад), що ми можемо і чого не можемо робити з цими символами, а також знаємо набір допустимих значень символів — наш алфавіт. У прикладі літери — і є тип даних. Ми знаємо безліч значень літер — вони є алфавітом. Крім того, знаємо, що літери ми можемо складати слова – це операції. Це природне уявлення людини лягло основою формального визначення типу даних.

Тип даних  – безліч значень та [допустимих] операцій над цими значеннями.

Хоча ми в житті не використовуємо це визначення і навіть не замислюємося про це, кожен день ми стикаємося з різними типами даних: літери, цифри, предмети побуту, продукти харчування. Але навіть при вирішенні побутових питань може виникнути ситуація, коли наявних у розпорядженні простих типів даних може бути недостатньо.

Уявіть собі такий діалог матері та маленького сина років трьох, який ще не дуже освоїв абстрактне мислення:

— Синку, принеси, будь ласка, зі столу ручку, мені треба записати щось!

– Мамо, тут немає ручки!

– Тоді олівець.

– І олівця немає.

– А що є?

— Фломастери є й маркер.

– Ну, тоді неси фломастер!

Доросла людина швидше за все відразу принесла б фломастер і сказала: «ручки не було, думаю, фломастер пригодиться». Чому? — Тому що з якогось рівня розвитку абстрактного мислення та вміння взаємодіяти з іншими людьми людина вже розуміє, що в цьому проханні головне не конкретний тип об’єкта — ручка — а її властивість писати по папері. Таку ж властивість мають й інші об’єкти: олівці, фломастери, маркери, навіть шматочок вугілля. Таким чином, він машинально поєднує кілька типів інструментів в один тип: те, чим можна писати. Це вміння часто спрощує нам повсякденне життя та спілкування з оточуючими, при цьому дещо віддаляючи нас від конкретних фізичних об’єктів. Щоб дістатися роботи, ми використовуємо «громадський транспорт» — маршрути та конкретні вагони чи машини можуть бути різними, але головне, що вони мають потрібні нам властивості: ми можемо увійти, вийти, сплатити за проїзд, ми заздалегідь впевнені в тому, що це можна зробити з кожним з об’єктів цього типу. Коли ми в незнайомому місті хочемо сходити в кафе, ми запитуємо у друзів або в інтернеті: «де в місті Н можна смачно поїсти?», тобто ми не визначаємо конкретний заклад, навіть його тип, а ставимо в основу найважливішу властивість групи закладів: ми можемо прийти туди та замовити їжу.

Іншими словами, у деяких ситуаціях нам важливий не об’єкт конкретного типу, а певні його властивості: тобто, що ми можемо з ним робити. Часто шуканими властивостями володіють кілька типів даних, і всі вони нам так чи інакше підійдуть, як описані вище приклади. Це призводить до визначення абстрактного типу даних. Спочатку наведемо формальне визначення, а потім розкриємо його у більш простих термінах.

Абстрактний тип даних (АТД)  – це математична модель для типів даних, де тип даних визначається поведінкою (семантикою) з точки зору користувача даних, а саме в термінах можливих значень, можливих операцій над даними цього типу та поведінки цих операцій.

Що таке математична модель? Хто такий користувач даних? Що мають на увазі під поведінкою операцій? Навіщо взагалі так ускладнювати? Давайте розумітися.

Заглиблюємося в деталі та розбираємо приклади

Щоб не перевантажувати себе новою інформацією, давайте будемо під математичною моделлю об’єкта або явища розуміти просто його формальний опис. Повертаючись, наприклад, з мамою, якій знадобилася ручка, ми можемо переробити її прохання в такий спосіб: «Сину, принеси мені, будь ласка, якийсь інструмент, який може залишати контрастні сліди на папері і при цьому поміщається до мене в руку». Звучить на побутовому рівні досить абсурдно, але саме це і буде формальним описом того, що знадобилося мамі.

Рухаємось далі. Уявімо програміста, перед яким стоїть завдання створення системи електронної черги в травмпункті.

При надходженні до травмпункту людині видається талон з номером (порядковим номером відвідувача травмпункту) та визначається пріоритетність черги: його травма оцінюється за шкалою від 1 до 10, де 10 – травма, яка потребує негайної медичної допомоги. Необхідно реалізувати механізм додавання людини до черги та визначення наступного пацієнта на прийом.

Для вирішення такого завдання потрібно створити програму, яка використовуватиме в коді деякі дані: номери талонів та їх пріоритети. У коді програми будуть проводитись операції над даними. Як ви побачите далі, ці операції не завжди прості, а можуть містити в собі безліч дій, а також мати деякі наслідки для всієї програми і всіх даних, що зберігаються: важкий пацієнт посуне всю чергу назад, тому що йому потрібна термінова медична допомога. Все в сукупності: набори операцій, дії, з яких вони складаються, а також їх наслідки називаються поведінкою операції.

Під користувачем даних ми в цій статті розумітимемо програміста, який використовує ці типи даних у своєму коді.

Залишається найважливіше питання: навіщо все це потрібне? Щоб відповісти на нього, розглянемо завдання докладніше.

Уявимо, що у черзі вже є талони з номерами 109, 101, 105, 112 з пріоритетами 5, 3, 1, 1 відповідно. Наступному пацієнту видається талон 113 з пріоритетом 3. Тоді цей талон «обходить» номери 105 і 112, тому що їхній пріоритет нижче, і встає позаду талона 101, з яким їх пріоритети рівні (мал. 1). Коли лікар готовий прийняти наступного пацієнта, то буде викликано пацієнта з номером 109 (мал. 2).

Новий пацієнт у черзі
Мал. 1. Новий пацієнт у черзі. 
Вибір наступного пацієнта у черзі.
Мал.2. Вибір наступного пацієнта у черзі.

Давайте напишемо код, який реалізує цю логіку. Будемо використовувати мову javascript . Спочатку опишемо звичайну чергу, без урахування пріоритету. Створимо клас PriorityQueue. Об’єкти цього класу міститимуть спочатку порожній масив data(створюється в конструкторі). Функція add_elemдодаватиме переданий їй параметр – номер талона – в кінець масиву data, а функція get_nextописуватиме виклик лікарем пацієнта на прийом: повертати значення першого елемента в масиві – номер пацієнта, який встав у чергу раніше за всіх інших, видаляючи з масиву цей елемент, так як , вирушаючи прийом, пацієнт залишає чергу. Також визначимо функцію print_que, яка виводитиме в консоль усі елементи черги по порядку.

class PriorityQueueconstructor(){ this.data = []; }

  add_elem(e){ this.data.push(e); }

  get_next() { return this.data.shift() }

  print_queue() {
      for(var i in this.data)	
          console.log(this.data[i]);  
  }
};

Наведемо приклад використання цього класу у коді програми:

Q = new PriorityQueue();
Q.add_elem(105);
Q.add_elem(101);
Q.add_elem(109);

console.log("next:",Q.get_next()) // next: 105
console.log("remains:") 
Q.print_queue(); // 101 109

Давайте модифікуємо клас, що вийшов, щоб черга враховувала пріоритет елемента. Для цього будемо в масиві dataзберігати не просто переданий елемент, а об’єкт із двома полями: значення ( value) та пріоритет ( priority). У коді такий об’єкт задаватиметься виразом виду var obj = {‘priority’: 1, ‘value’: 112};Перепишемо функцію додавання елемента в чергу:


add_elem(e,p)  {		
    
  for (var i = this.data.length - 1; i >= 0; i--) {		
 
    if(this.data[i].priority >= p){                
 		     
      this.data.splice(i+1,0,{'value':e, 'priority':p});                
      return;            
    }     
  }
     
  this.data.splice(0,0,{'value':e, 'priority':p});  
}

Змоделюємо ситуацію, проілюстровану малюнками 1 і 2.

Q = new PriorityQueue();
Q.add_elem(109,5);
Q.add_elem(101,3);
Q.add_elem(105,1);
Q.add_elem(112,1);
Q.add_elem(113,3);

console.log("next:",Q.get_next()) // next: { value: 109, priority: 5 }
console.log("remains:")
Q.print_queue(); // value: 101, priority: 3 }
                 //{ value: 113, priority: 3 }
                 //{ value: 105, priority: 1 }
                 //{ value: 112, priority: 1 }

Можлива модифікація: якщо пацієнт перебуває в черзі протягом тривалого часу, наприклад, за цей час лікар уже прийняв як мінімум одного пацієнта з черги, то пріоритет його талона підвищується на 1, проте не може перевищити 8, щоб залишити можливість термінового прийому важких хворих.

Для цього змінимо функцію вибору наступного елемента get_next.

get_next() {       	
  var e = this.data.shift();	
  for(var i in this.data){ 
    	
    if(this.data[i].priority<8) 
      this.data[i].priority++; 
  }	
  return e; 
}

Можемо помітити, що в даному випадку при вилученні елемента з черги змінюються всі елементи. Це і буде та сама «поведінка операції», про яку йшлося раніше.

Код програми, що вийшов, можна умовно розділити на дві частини: опис класу PriorityQueue і використання цього класу. Зауважимо, що використання класу виглядає досить лаконічно, порівняно з його описом. Працюючи з класом та її функціями програміст, який виступає у разі як користувач класу, може мати уявлення, як саме реалізована логіка роботи всередині функцій класу. Для його роботи достатньо того, що ця логіка відповідає заявленому опису пріоритетної черги. У такому разі кажуть, що клас реалізує певний інтерфейс  – набір методів роботи з класом, де зафіксовано вхідні та вихідні параметри. У цьому прикладі це методи add_elemget_next і print_queue. Можна змінити внутрішню логіку роботи цих методів, якщо цього вимагатиме завдання, як це було у разі підвищення пріоритету при довгому очікуванні. Однак для користувача класу ці зміни не будуть помітними і не вимагатимуть зміни коду основної програми.

Бонус. Що таке структура даних?

У програмуванні також є поняття структури даних, яке певною мірою близьке за змістом типу даних.

У чому різниця? Структура даних – це конкретна програмна реалізація типу даних. Якщо двох людей попросити реалізувати пріоритетну чергу, швидше за все навіть при використанні однієї мови програмування їх реалізації відрізнятимуться, хоча обидві відповідатимуть заявленому інтерфейсу. Тобто вони створять дві різні структури даних.

Структура даних (англ. data structure) — програмна одиниця, що дозволяє зберігати та обробляти безліч однотипних та/або логічно пов’язаних даних у обчислювальній техніці. Для додавання, пошуку, зміни та видалення даних структура даних надає деякий набір функцій, що становлять її інтерфейс.

Висновок

Підіб’ємо підсумки виконаної роботи. Клас PriorityQueueмає такі властивості:

  1. Він може зберігати у собі будь-які елементи з пріоритетом, який задається числом.
  2. Визначено інтерфейс взаємодії з класом: методи add_elemget_nextі print_queue.
  3. Описані методи реалізують заявлену логіку роботи пріоритетної черги.
  4. Методи мають певну поведінку (див., наприклад, бонус).

Таким чином, ми створили свій абстрактний тип даних та реалізували його мовою javascript . Що ми від цього виграли:

  1. Код основної програми лаконічний і зрозумілий, тому що не містить технічних подробиць внутрішньої роботи черги.
  2. Зручно вносити зміни до логіки роботи черги, не торкаючись основного коду.
  3. Клас PriorityQueueможе бути перевикористаний в інших програмах, де потрібна схожа логіка.
  4. У разі командної роботи можна поділити між людьми роботу над класом та розробку з використанням цього класу. Сполучною ланкою буде інтерфейс класу.

Незважаючи на те, що розробка абстрактного типу даних сама по собі може бути досить трудомісткою, використання АТД здорово допомагає заощадити сили і час при роботі зі складними завданнями. Принцип «розділяй і владарюй» працює тут якнайкраще: створення додаткового рівня абстракції допомагає розробнику розбити складне завдання на дрібніші і простіші, а його колегам спрощує розуміння коду програми, що надзвичайно важливо в командній розробці.

Звичайно, АТД – це не панацея, яка допоможе зробити будь-який код прекрасним, читаним і зрозумілим, уникнувши всіх помилок. Дуже часто навіть досвідчені програмісти у своєму прагненні зробити все правильно та красиво заходять надто далеко в нетрі абстракцій, що навпаки все ускладнює. Коли варто створювати нову абстракцію, а коли це зайве, зрозуміти можна лише з досвідом. Універсального рецепта немає, можна лише порадити намагатися періодично переглядати свій старий код, щоб зробити висновки, що з написаного робить його зрозумілішим, а що через деякий час незрозуміло вже навіть вам самим.

Пам’ятайте, що у висловлювання Девіда Віллера і є і друга, менш відома частина:

У комп’ютерній науці всі проблеми можна вирішити з допомогою додаткового рівня непрямості. Але зазвичай це створює іншу проблему

Джерело

Більше 40% користувачів засумнівалися у етичному застосуванні ШІ

ШІ

У споживачів зростає недовіра до ШІ фірм — понад 40% респондентів не вірять, що компанії використовують технологію етично. Про це свідчать результати опитування Salesforce.

У дослідженні взяли участь понад 14 000 споживачів та фірм у 25 країнах.

Майже 70% із них заявили, що в міру розвитку ШІ профільним компаніям варто підвищувати довіру до своєї діяльності.

За даними Salesforce, протягом року респонденти стали менш схильні до використання штучного інтелекту. Якщо у 2022 році понад 80% бізнес-покупців та 65% споживачів були готові використати технологію для покращення якості обслуговування — зараз обидва показники впали до 73% та 51% відповідно.

Окреме опитування майже 1500 австралійців від дослідницької фірми Roy Morgan показало, що близько 60% із них впевнені: ШІ «створює більше проблем, ніж вирішує».

Кожен п’ятий респондент пов’язує із цими розробками причину потенційного зникнення людства до 2043 року.

Roy Morgan
Дані: Roy Morgan.

Третє дослідження від Pew Research, у якому взяли участь понад 5000 американців, виявив досить низьку поінформованість людей про ChatGPT. Лише 18% із них використали цього чат-бота.

Pew Research
Дані: Pew Research.

На тлі глобальних дебатів щодо необхідності законодавчого регулювання ШІ майже 70% респондентів виступили за введення більшої кількості правил.

Нагадаємо, згідно з опитуванням CFP, третина інвесторів із США довіряють фінансовим рекомендаціям від штучного інтелекту, не звіряючи їх з іншими джерелами.

Раніше LinkedIn вказав на зростання числа професіоналів із навичками роботи в галузі ШІ.

Джерело

Патерни реактивності у сучасному JavaScript

 JavaScript

“Реактивність” – це те, як системи реагують на оновлення даних. Існують різні типи реактивності, але в рамках цієї статті реактивність — це коли ми щось робимо у відповідь на зміну даних.

Патерни реактивності є ключовими для веб-розробки

Ми працюємо з великою кількістю JS на сайтах та у веб-додатках, оскільки браузер – це повністю асинхронне середовище. Ми повинні реагувати на дії користувача, взаємодіяти з сервером, надсилати звіти, моніторити продуктивність тощо. Це включає оновлення UI, мережеві запити, зміни навігації та URL в браузері, що робить каскадне оновлення даних ключовим аспектом веб-розробки.

Реактивність зазвичай асоціюється з фреймворками, але можна багато чому навчитися, реалізуючи реактивність на чистому JS. Ми можемо змішувати та грати з цими патернами для кращої обробки оновлення даних.

Вивчення патернів призводить до зменшення кількості коду та підвищення продуктивності веб-застосунків, незалежно від використовуваного фреймворку.

Мені подобається вивчати патерни, оскільки вони застосовні до будь-якої мови та системи. Паттерни можуть комбінуватися для вирішення завдань конкретної програми, часто приводячи до більш продуктивного і підтримуваного коду.

Видавець/передплатник

Видавець/передплатник (Publisher/Subscriber, PubSub) — один із основних патернів реактивності. Виклик події за допомогою publish() дозволяє передплатникам (підписалися на подію за допомогою subscribe()) реагувати на зміну даних:

const pubSub = {
  events: {},
  subscribe(event, callback) {
    if (!this.events[event]) {
      this.events[event] = []
    }
    this.events[event].push(callback)
  },
  publish(event, data) {
    if (this.events[event]) {
      this.events[event].forEach((callback) => {
        callback(data)
      })
    }
  },
 
  unsubscribe(event, callback) {
    if (this.events[event]) {
      this.events[event] = this.events[event].filter((cb) => cb !== callback)
    }
  }
}

const handleUpdate = (data) => {
  console.log(data)
}
pubSub.subscribe('update', handleUpdate)
pubSub.publish('update', 'Some update') // Some update
pubSub.unsubscribe('update', handleUpdate)
pubSub.publish('update', 'Some update') // nothing

Кастомні події – нативний браузерний інтерфейс для PubSub

Браузер надає API для виклику та передплати кастомних подій (custom events). Метод dispatchEvent() дозволяє не тільки викликати подію, але й прикріплювати до неї дані:

const pizzaEvent = new CustomEvent('pizzaDelivery', {
  detail: {
    name: 'Supreme',
  },
})

const handlePizzaEvent = (e) => {
  console.log(e.detail.name)
}
window.addEventListener('pizzaDelivery', handlePizzaEvent)
window.dispatchEvent(pizzaEvent) // Supreme

Ми можемо обмежити область видимості (scope) кастомної події будь-яким вузлом DOM. У наведеному прикладі ми використовували глобальний об’єкт window, який також відомий як глобальна шина подій (event bus).

<div id="pizza-store"></div>
const pizzaEvent = new CustomEvent('pizzaDelivery', {
  detail: {
    name: 'Supreme',
  },
})

const pizzaStore = document.getElementById('pizza-store')
const handlePizzaEvent = (e) => {
  console.log(e.detail.name)
}
pizzaStore.addEventListener('pizzaDelivery', handlePizzaEvent)
pizzaStore.dispatchEvent(pizzaEvent) // Supreme

Примірники кастомних подій – створення підкласів EventTarget

Ми можемо створювати підкласи мети події (event target) для надсилання подій до екземпляра класу:

class PizzaStore extends EventTarget {
  constructor() {
    super()
  }
  addPizza(flavor) {          
    this.dispatchEvent(
      new CustomEvent('pizzaAdded', {
        detail: {
          pizza: flavor,
        },
      }),
    )
  }
}

const Pizzas = new PizzaStore()
const handleAddPizza = (e) => {
  console.log('Added pizza:', e.detail.pizza)
}
Pizzas.addEventListener('pizzaAdded', handleAddPizza)
Pizzas.addPizza('Supreme') // Added pizza: Supreme

Наші події викликаються на класі, а не глобально window. Обробники можуть безпосередньо підключатися до цього примірника.

Спостерігач

Паттерн “Спостерігач” (Observer) схожий на PubSub. Він дозволяє підписуватись на суб’єкта (Subject). Для повідомлення передплатників про зміну даних суб’єкт викликає метод notify():

class Subject {
  constructor() {
    this.observers = []
  }
  addObserver(observer) {
    this.observers.push(observer)
  }
  removeObserver(observer) {
    this.observers = this.observers.filter((o) => o !== observer)
  }
  notify(data) {
    this.observers.forEach((observer) => {
      observer.update(data)
    })
  }
}

class Observer {
  update(data) {
    console.log(data)
  }
}

const subject = new Subject()
const observer = new Observer()

subject.addObserver(observer)
subject.notify('Hi, observer!') // Hi, observer!
subject.removeObserver(observer)
subject.notify('Are you still here?') // nothing

Реактивні властивості об’єкта – проксі

Proxy дозволяє забезпечити реактивність при встановленні/отриманні значень властивостей об’єкта:

const handler = {
  get(target, property) {
    console.log(`Getting property ${property}`)
    return target[property]
  },
  set(target, property, value) {
    console.log(`Setting property ${property} to value ${value}`)
    target[property] = value
    return true 
  },
}

const pizza = {
  name: 'Margherita',
  toppings: ['mozzarella', 'tomato sauce'],
}
const proxiedPizza = new Proxy(pizza, handler)

console.log(proxiedPizza.name) // 'Getting property name' и 'Margherita'
proxiedPizza.name = 'Pepperoni' // Setting property name to value Pepperoni

Реактивність окремих властивостей об’єкту

Object.defineProperty() дозволяє визначати аксесори (гетери та сеттери) при визначенні властивості об’єкта:

const pizza = {
  _name: 'Margherita',
}

Object.defineProperty(pizza, 'name', {
  get() {
    console.log('Getting property name')
    return this._name
  },
  set(value) {
    console.log(`Setting property name to value ${value}`)
    this._name = value
  },
})

console.log(pizza.name) // 'Getting property name' и 'Margherita'
pizza.name = 'Pepperoni' // Setting property name to value Pepperoni

Object.defineProperties() дозволяє визначити аксесори для кількох властивостей об’єкта одночасно.

Асинхронні реактивні дані – проміси

Давайте зробимо наших спостерігачів асинхронними! Це дозволить оновлювати дані та запускати спостерігачів асинхронно:

class AsyncData {
  constructor(initialData) {
    this.data = initialData
    this.subscribers = []
  }

  
  subscribe(callback) {
    if (typeof callback !== 'function') {
      throw new Error('Callback must be a function')
    }
    this.subscribers.push(callback)
  }

 
  async set(key, value) {
    this.data[key] = value

    const updates = this.subscribers.map(async (callback) => {
      await callback(key, value)
    })

    await Promise.allSettled(updates)
  }
}

const data = new AsyncData({ pizza: 'Pepperoni' })

data.subscribe(async (key, value) => {
  await new Promise((resolve) => setTimeout(resolve, 1000))
  console.log(`Updated UI for ${key}: ${value}`)
})

data.subscribe(async (key, value) => {
  await new Promise((resolve) => setTimeout(resolve, 500))
  console.log(`Logged change for ${key}: ${value}`)
})

async function updateData() {
  await data.set('pizza', 'Supreme') 
  console.log('All updates complete.')
}

updateData()
/**
  через 500 мс
  Logged change for pizza: Supreme
  через 1000 мс
  Updated UI for pizza: Supreme
  All updates complete.
 */

Реактивні системи

В основі багатьох популярних бібліотек і фреймворків лежать складні реактивні системи: хуки (Hooks) в React, сигнали (Signals) в SolidJS, сутності (Observables) в Rx.js і т.д. Як правило, їх основним завданням є повторний рендеринг компонентів або фрагментів DOM при зміні даних.

Observables (Rx.js)

Паттерн “Спостерігач” і Observables (що можна умовно перекласти як “сутні, що спостерігаються”) – це не одне і те ж, як може здатися на перший погляд.

Observables дозволяють генерувати (produce) послідовність (sequence) значень протягом часу. Розглянемо простий примітив Observable, що відправляє послідовність значень передплатникам, дозволяючи їм реагувати на значення, що генеруються:

class Observable {
  constructor(producer) {
    this.producer = producer
  }

 
  subscribe(observer) {
    
    if (typeof observer !== 'object' || observer === null) {
      throw new Error('Observer must be an object with next, error, and complete methods')
    }

    if (typeof observer.next !== 'function') {
      throw new Error('Observer must have a next method')
    }

    if (typeof observer.error !== 'function') {
      throw new Error('Observer must have an error method')
    }

    if (typeof observer.complete !== 'function') {
      throw new Error('Observer must have a complete method')
    }

    const unsubscribe = this.producer(observer)

   
    return {
      unsubscribe: () => {
        if (unsubscribe && typeof unsubscribe === 'function') {
          unsubscribe()
        }
      },
    }
  }
}

Приклад використання:


const observable = new Observable(observer => {
  observer.next(1)
  observer.next(2)
  observer.next(3)
  observer.complete()

  return () => {
    console.log('Observer unsubscribed')
  }
})

const observer = {
  next: value => console.log('Received value:', value),
  error: err => console.log('Error:', err),
  complete: () => console.log('Completed'),
}

const subscription = observable.subscribe(observer)

subscription.unsubscribe()

Метод next()надсилає дані спостерігачам. Метод complete()закриває потік даних (stream). Метод error()призначений обробки помилок. subscribe()дозволяє передплатити дані, а unsubscribe()— відписатися від них.

Найпопулярнішими бібліотеками, в яких використовується цей патерн, є Rx.js та MobX .

Signals (SolidJS)

Погляньте на курс реактивності з SolidJS від Ryan Carniato.

const context = []

export function createSignal(value) {
  const subscriptions = new Set()

  const read = () => {
    const observer = context[context.length - 1]
    if (observer) {
      subscriptions.add(observer)
    }
    return value
  }
  const write = (newValue) => {
    value = newValue
    for (const observer of subscriptions) {
      observer.execute()
    }
  }

  return [read, write]
}

export function createEffect(fn) {
  const effect = {
    execute() {
      context.push(effect)
      fn()
      context.pop()
    },
  }

  effect.execute()
}

Приклад використання:

import { createSignal, createEffect } from './reactive'

const [count, setCount] = createSignal(0)

createEffect(() => {
  console.log(count())
}) // 0

setCount(10) // 10

Повний код прикладу можна знайти тут.

Значення, що спостерігаються (Frontend Masters)

Наш відеоплеєр має багато налаштувань, які можуть змінюватись у будь-який час для модифікації відтворення відео. Kai з нашої команди розробив значення, що спостерігаються (observable-ish values), що являє собою ще один приклад реактивної системи на чистому JS.

Значення, що спостерігаються – це поєднання PubSub з обчислюваними значеннями (computed values), що дозволяють складати результати декількох видавців.

Приклад повідомлення передплатника про зміну значення:

const fn = function (current, previous) {}

const obsValue = ov('initial')
obsValue.subscribe(fn) 
obsValue() // 'initial'
obsValue('initial') // 'initial',
obsValue('new') // fn('new', 'initial')
obsValue.value = 'silent' //

Модифікація масивів та об’єктів не публікує зміни, а замінює їх:

const obsArray = ov([1, 2, 3])
obsArray.subscribe(fn)
obsArray().push(4) // 
obsArray.publish() // fn([1, 2, 3, 4]);
obsArray([4, 5]) // fn([4, 5], [1, 2, 3]);

Передача функції кешує результат як значення. Додаткові аргументи передаються функції. Спостережені сутності, викликані функції, є передплатниками, оновлення цих сутностей призводить до повторного обчислення значення.

Якщо функція повертає проміс, значення надається асинхронно після його вирішення.

const a = ov(1)
const b = ov(2)
const computed = ov((arg) => {
  a() + b() + arg
}, 3)
computed.subscribe(fn)
computed() // fn(6)
a(2) // fn(7, 6)

Реактивний рендеринг UI

Розглянемо деякі патерни читання та записи в DOM та CSS.

Рендеринг даних за допомогою шаблонних літералів

Шаблонні літерали (template literals) дозволяють виконувати інтерполяцію змінних, що полегшує генерацію шаблонів HTML:

function PizzaRecipe(pizza) {
  return `<div class="pizza-recipe">
    <h1>${pizza.name}</h1>
    <h3>Toppings: ${pizza.toppings.join(', ')}</h3>
    <p>${pizza.description}</p>
  </div>`
}

function PizzaRecipeList(pizzas) {
  return `<div class="pizza-recipe-list">
    ${pizzas.map(PizzaRecipe).join('')}
  </div>`
}

const allPizzas = [
  {
    name: 'Margherita',
    toppings: ['tomato sauce', 'mozzarella'],
    description: 'A classic pizza with fresh ingredients.',
  },
  {
    name: 'Pepperoni',
    toppings: ['tomato sauce', 'mozzarella', 'pepperoni'],
    description: 'A favorite among many, topped with delicious pepperoni.',
  },
  {
    name: 'Veggie Supreme',
    toppings: [
      'tomato sauce',
      'mozzarella',
      'bell peppers',
      'onions',
      'mushrooms',
    ],
    description: 'A delightful vegetable-packed pizza.',
  },
] 

 function renderPizzas() {
  document.querySelector('body').innerHTML = PizzaRecipeList(allPizzas)
}

renderPizzas() /


function addPizza() {
  allPizzas.push({
    name: 'Hawaiian',
    toppings: ['tomato sauce', 'mozzarella', 'ham', 'pineapple'],
    description: 'A tropical twist with ham and pineapple.',
  })

  renderPizzas() 
}

addPizza()

Основним недоліком цього підходу є модифікація всього DOM при кожному рендерингу. Такі бібліотеки, як lit-html , дозволяють оновлювати DOM інтелектуальніше, коли оновлюються лише модифіковані частини.

Реактивні атрибути DOM – MutationObserver

Одним із способів забезпечення реактивності DOM є маніпулювання атрибутами HTML-елементів. MutationObserver API дозволяє спостерігати за зміною атрибутів і реагувати на них певним чином:

const mutationCallback = (mutationsList) => {
  for (const mutation of mutationsList) {
    if (
      mutation.type !== 'attributes' ||
      mutation.attributeName !== 'pizza-type'
    )
      return

    console.log('Old:', mutation.oldValue)
    console.log('New:', mutation.target.getAttribute('pizza-type'))
  }
}
const observer = new MutationObserver(mutationCallback)
observer.observe(document.getElementById('pizza-store'), { attributes: true })

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

Реактивні атрибути у веб-компонентах

Веб-компоненти (Web Components) надають нативний спосіб спостереження за оновленнями атрибутів:


class PizzaStoreComponent extends HTMLElement {
  static get observedAttributes() {
    return ['pizza-type']
  }

  constructor() {
    super()
    const shadowRoot = this.attachShadow({ mode: 'open' })
    shadowRoot.innerHTML = `<p>${
      this.getAttribute('pizza-type') || 'Default content'
    }</p>`
  }

  attributeChangedCallback(name, oldValue, newValue) {
    if (name === 'pizza-type') {
      this.shadowRoot.querySelector('div').textContent = newValue
      console.log(`Attribute ${name} changed from ${oldValue} to ${newValue}`)
    }
  }
}

customElements.define('pizza-store', PizzaStoreComponent)

<pizza-store pizza-type="Supreme"></pizza-store>

document.querySelector('pizza-store').setAttribute('pizza-type', 'BBQ Chicken');

Реактивне прокручування — IntersectionObserver

IntersectionObserver API дозволяє реагувати на перетин цільового елемента з іншим елементом або областю перегляду (viewport):

const pizzaStoreElement = document.getElementById('pizza-store')

const observer = new IntersectionObserver((entries) => {
  entries.forEach((entry) => {
    if (entry.isIntersecting) {
      entry.target.classList.add('animate-in')
    } else {
      entry.target.classList.remove('animate-in')
    }
  })
})

observer.observe(pizzaStoreElement)

Дивіться приклад анімації під час прокручування на CodePen .

Прим. пер.: Крім MutationObserverі IntersectionObserverіснує ще один нативний спостерігач – ResizeObserver .

Зациклювання анімації – requestAnimationFrame

При розробці ігор, при роботі з Canvas або WebGL анімації часто вимагають запису в буфер і наступного запису результатів у циклі, коли потік рендерингу стає доступним. Зазвичай, ми реалізуємо це за допомогою requestAnimationFrame:

function drawStuff() {
  // Логіка рендерингу гри чи анімації
}

// Функция обробки анімації
function animate() {
  drawStuff()
  requestAnimationFrame(animate) // Продолжаем вызывать `animate` на каждом кадре рендеринга
}

// Запускаєм анімацію
animate()

Реактивні анімації – Web Animations

Web Animations API дозволяє створювати реактивні гранульовані анімації. Приклад використання цього інтерфейсу для анімації масштабу, положення та кольору елемента:

const el = document.getElementById('animated-element')

// Визначаємо властивості анімації
const animation = el.animate(
  [
    // Ключові кадри (keyframes)
    {
      transform: 'scale(1)',
      backgroundColor: 'blue',
      left: '50px',
      top: '50px',
    },
    {
      transform: 'scale(1.5)',
      backgroundColor: 'red',
      left: '200px',
      top: '200px',
    },
  ],
  {
    // Налаштування часу
    // Тривалість
    duration: 1000,
    // Напрямок
    fill: 'forwards',
  },
)

// Встановлюємо швидкість відтворення у значення `0`
// для призупинення анімації
animation.playbackRate = 0

// Реєструємо оброблювач кліку
el.addEventListener('click', () => {
  // Якщо анімацію припинено, відновлюємо її
  if (animation.playbackRate === 0) {
    animation.playbackRate = 1
  } else {
    // Якщо анімація відтворюється, змінюємо її напрямок
    animation.reverse()
  }
})

Реактивність такої анімації полягає в тому, що вона може відтворюватись щодо поточного положення в момент взаємодії (як у разі зміни напряму в наведеному прикладі). Анімації та переходи CSS такого робити не дозволяють.

Реактивний CSS – кастомні властивості та calc

Ми можемо писати реактивний CSS за допомогою кастомних властивостей та calc:

barElement.style.setProperty('--percentage', newPercentage)

Ми встановлюємо значення кастомного властивості JS.

.bar {
  width: calc(100% / 4 - 10px);
  height: calc(var(--percentage) * 1%);
  background-color: blue;
  margin-right: 10px;
  position: relative;
}

І робимо обчислення на основі цього значення в CSS. Таким чином, за стилізацію елемента повністю відповідає CSS, як і має бути.

Прочитати поточне значення кастомної властивості можна так:

getComputedStyle(barElement).getPropertyValue('--percentage')

Як бачите, сучасний JS дозволяє досягати реактивності безліччю різних способів. Ми можемо комбінувати ці патерни для реактивного рендерингу, логування, анімації, обробки подій користувача та інших речей, що відбуваються в браузері.

Джерело

RxJS: як оператори вищого порядку спрощують код

RxJS

Якщо ви працювали з Angular, то, напевно, зустрічалися з RxJS. Потоки, розлогі конструкції, багато аргументів у методу pipe, а кожен аргумент повертають різні функції з різною кількістю аргументів. Існують інтуїтивно зрозумілі функції типу filter або map. Перший явно фільтрує значення потоці, а другий ці значення змінює. Такі функції називають операторами. І чим глибше ви провалюєтеся в RxJS, тим більше різних операторів ви дізнаєтеся. І з часом дістаєтеся потоків потоків. Тобто замість звичайних значень такий потік емітує інші потоки. Такі потоки називають Higher Order Observables. І для роботи з такими потоками є спеціальні оператори. Можливо, ви чули, що такі оператори називають Higher Order Operators (HOO). Вони можуть вирівнювати потоки або, іншими словами, робити їх звичайним.

У цій статті я покажу, що в HOO немає нічого міфічного, і розповім, у яких випадках вам потрібно використовувати оператори вищого порядку. Зараз ви подумаєте, що це нудний лонгрід, але не поспішайте. Ми розглянемо всього 4 оператори: switchMapexhaustMapconcatMapі mergeMap.

switchMap

switchMapОднозначно найпопулярніший з усіх. Але чому? А з однієї просто причини — цей оператор позбавляє нас від стану гонки, що дуже часто зустрічається.

Давайте розглянемо код:

const searchInput = document.getElementById('search-input');
const search$ = fromEvent(searchInput, 'input').pipe(
  map(event => event.target.value)
);

search$.subscribe((query) => {
  http.get('/search', {params: {query}}).subcribe((result) => {
    console.log(result);
  })
})

У цьому коді ми знаходимо input, з яким взаємодіє користувач і підписуємось на подію input. Тобто потік search$ емітит рядка. Усередині підписки бачимо, що у кожен еміт рядка відправляється запит на сервер і відповідь сервісу виводиться у консоль.

У цьому коді можна побачити щонайменше дві проблеми:

  1. Race state.  Зазвичай при пошуку чогось користувача важливо бачити результат саме останнього запиту. Але код такого виду не дає нам гарантії, що останні дані, виведені в консоль, відповідають останньому рядку, що випускається в потоці search$.
  2. Subscribe in subscribe та жодного unsubscribe. Є дуже хороше правило, за яким можна позбавити себе багатьох проблем, — на кожен subscrib має бути unsubscribe. Це щонайменше знизить ймовірність витоку пам’яті.

Але давайте подумаємо, як має працювати пошук:

  1. У момент еміту рядка перевірити наявність активних запитів
  2. Якщо запитів немає, перейти до пункту 4
  3. Відписатися від попередніх запитів
  4. Передплатити новий запит
  5. Вивести у консоль

У лоб це можна реалізувати так:

const searchInput = document.getElementById('search-input');

const search$ = fromEvent(searchInput, 'input').pipe(
  map(event => event.target.value)
);


let subscription;
search$.subscribe((query) => {
  if (subscription) {
    subscription.unsubscribe();
  }

  subscription = http.get('/search', {params: {query}}).subcribe((result) => {
    console.log(result);
  })
})

Але якщо я вам скажу, що перші 4 пункти вимог робить оператор switchMap? Давайте перепишемо код:

const searchInput = document.getElementById('search-input');
const search$ = fromEvent(searchInput, 'input').pipe(
  map(event => event.target.value)
);

search$.pipe(
  switchMap((query) => http.get('/search', {params: {query}}))
).subscribe((result) => {
  console.log(result);
});

switchMapгарантує нам, що ми завжди будемо отримувати результати останнього потоку та позбавляє нас race state. Ну і приємний бонус полягатиме в тому, що відмовившись від зовнішньої підписки автоматично відбудеться і відписка від внутрішньої. Профіт!

Резюмуємо. switchMapможна використовувати у випадках, коли нам важливим є результат останньої дії, наприклад, при пошуку або реалізації нескінченного скрола. Якщо всі попередні дії можуть не враховуватися, можна сміливо брати switchMap.

exhaustMap

exhaustMapОднозначно найпопулярніший з усіх. Причина мені не до кінця зрозуміла, але за допомогою нього можна реалізувати пошук, але використовуючи інший підхід.

const searchInput = document.getElementById('search-input');
const searchButton = document.getElementById('search-button');

const searchButtonClick$ = fromEvent(searchButton, 'click');

searchButtonClick$.subscribe((result) => {
  const query = searchInput.value;
  http.get('/search', { params: { query } }).subscribe((result) => {
    console.log(result);
  });
});

У цьому коді ініціатор запит – клік по кнопці.

Власне цей код має такі ж проблеми, як і в попередньому прикладі, але тут у нас інші вимоги:

  1. У момент натискання на кнопку перевірити наявність активних запитів
  2. Якщо є активний, нічого не робити та чекати наступного кліку
  3. Якщо немає активного, підписатися на виконання запиту
  4. Вивести результат у консоль

Реалізуємо в лоб:

const searchInput = document.getElementById('search-input');
const searchButton = document.getElementById('search-button');

const searchButtonClick$ = fromEvent(searchButton, 'click');

let isRequestPending = false;
searchButtonClick$.subscribe((result) => {
  if (isRequestPending) {
    return;
  }

  isRequestPending = true;
  const query = searchInput.value;
  http.get('/search', { params: { query } }).subscribe((result) => {
    isRequestPending = false;
    console.log(result);
  });
});

Як і в першому випадку нам знадобилася ще одна змінна поза потоком. Це додає коду складності і якщо ми захочемо змінити поведінку потоку, все доведеться переписати. Ну і як ви вже здогадалися на допомогу нам приходить exhaustMap.

const searchInput = document.getElementById('search-input');
const searchButton = document.getElementById('search-button');

const searchInput$ = fromEvent(searchButton, 'click');

searchInput$.pipe(
  exhaustMap(() => {
    const query = searchInput.value;
    return http.get('/search', { params: { query } });
  })
).subscribe((result) => {
  console.log(result);
});

Резюмуємо. exhaustMapпотрібно використовувати у випадках, коли при активній підписці на потік інші можна ігнорувати, як у разі пошуку натискання кнопки або, наприклад, при пропуску подій початку анімації при її відтворенні.

mergeMap

mergeMap– Оператор, який об’єднує всі внутрішні потоки в один вихідний потік. Це означає, що внутрішні потоки можуть завершуватись у будь-якому порядку, і їх результати будуть об’єднані разом. І це найпростіше пояснення, яке я зміг із себе видавити.

Давайте перейдемо до прикладів:

entityId$.subscribe((id) => {
  entityService.get(id).subscribe(() => {
    entityStore.upsertItem(entity);
  });
});

У цьому коді ми бачимо entityId$ – це потік рядків з id певної сутності. Тут ми повинні на кожен id запросити дані щодо сутності з сервера і додати або оновити цю сутність в стор. Власне, саме це наш код і робить і вирішувати тут нема чого. Але проблеми є і в цьому випадку вони абсолютно ідентичні попереднім. Давайте спробуємо ускладнити завдання і ввести обмеження в 3 запити одночасно.

Вирішуємо:

const CONCURRENT_REQUESTS = 3;
let activeRequests = 0;
const queue = [];

function processNext() {
  if (queue.length === 0 || activeRequests >= CONCURRENT_REQUESTS) {
    return;
  }

  const id = queue.shift();
  activeRequests++;

  entityService.get(id).subscribe(entity => {
    entityStore.upsertItem(entity);
    activeRequests--;

    processNext();
  });
}

entityId$.subscribe(id => {
  queue.push(id);
  processNext();
});

Я навіть не намагався перевіряти код на працездатність, бо написав його у редакторі тексту. Код став складно-читаним. Функція processNextмає кілька побічних ефектів усередині. А ще є додаткові змінні за межами потоку та функції. Скласти все це воєдино досить складно.

Саме такі завдання вирішує mergeMap. Давайте перепишемо перший приклад із використанням цього оператора:

entityId$.pipe(
  mergeMap((id) => entityService.get(id))
).subscribe((entity) => {
  entityStore.upsertItem(entity);
});

У цьому коді mergeMapпідписується на кожен потік, повернутий entityService.get(id)і їх значення видає в одному єдиному потоці.

Добре, а як бути з обмеженням 3 запити в один момент часу? Виявляється, що mergeMapвже й так усе вміє. Другий аргумент у mergeMapприймає число, яке якраз налаштовує конкурентність.

entityId$.pipe(
  mergeMap((id) => entityService.get(id), 3)
).subscribe((entity) => {
  entityStore.upsertItem(entity);
});

От і все!

Резюмуємо. mergeMapвідмінно підходить, коли ви хочете виконувати паралельні дії та поєднувати їх результати. Однак, слід бути обережним, оскільки може виникнути багато активних запитів, якщо вихідний потік випромінює значення занадто швидко.

concatMap

concatMap– Останній оператор вищого порядку. Ключова відмінність полягає в тому, що concatMap підтримує порядок виконання. Він дочекається завершення одного внутрішнього потоку, перш ніж перейде до наступного.

Щоб практично подивитися на його використання, ми можемо взяти попередній приклад і змінити до нього вимоги. Так вийшло, що нас перестало влаштовувати невпорядкованість запитів, і ми хочемо виконувати їх не паралельно, а по черзі. Тобто конкурентність має стати рівною одиниці.

entityId$.pipe(
  mergeMap((id) => entityService.get(id), 1)
).subscribe((entity) => {
  entityStore.upsertItem(entity);
});

Але mergeMap з конкурентністю 1 робить те ж саме, що і concatMap! Буквально. Це чудово видно у вихідному коді оператора.

concatMap

Тобто використання mergeMap із конкурентністю 1 на стільки частий кейс, що його винесли в окремий оператор.

Резюмуємо. concatMapчудово підходить для ситуацій, коли порядок виконання важливий. Якщо ви хочете обробити послідовність дій без паралельної обробки, це ваш вибір.

Висновок

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

  • Оператор switchMap чудово підходить, коли нам важливий лише останній результат випромінювання, наприклад, у разі пошуку у реальному часі.
  • exhaustMap ідеальний для випадків, коли нам потрібно ігнорувати нові об’єкти, що спостерігаються, до завершення поточного.
  • mergeMap дозволяє обробляти кілька вхідних об’єктів, що спостерігаються паралельно, але може призвести до перевантаження, якщо не контролювати кількість одночасних потоків.
  • concatMap гарантує порядок обробки, виконуючи кожен внутрішній об’єкт, що спостерігається послідовно.

При правильному використанні ці оператори можуть справлятися з безліччю реактивних завдань, будь то події з інтерфейсу користувача, HTTP-запити або навіть складні анімаційні послідовності.

Однак ключове слово тут – правильне використання . Завжди аналізуйте вимоги вашої програми та ретельно вибирайте відповідний оператор. Це допоможе уникнути небажаних побічних ефектів та створити реактивні рішення, які можуть масштабуватися та легко підтримуватися.

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

Джерело

Третина інвесторів у США заявила про довіру фінансовим радам ШІ

ШІ

Близько 31% інвесторів із США довіряють фінансовим рекомендаціям від штучного інтелекту (ШІ), не звіряючи їх з іншими джерелами. Про це свідчить опитування CFP .

Близько третини із 1153 респондентів фактично отримували поради щодо планування бюджету від нейромереж, при цьому 80% з них відзначили високий рівень задоволеності цим досвідом.

ШІ

Більше половини опитаних зазначають, що їм все ж таки зручніше користуватися підказками, створеними ШІ та в соціальних мережах, якщо вони перевірені експертами. 52% відзначили бажання скористатися порадами від роботів у майбутньому.

«За останнє десятиліття кількість неперевірених інвестиційних рекомендацій на таких платформах, як TikTok та Instagram різко зросла. Інвестори мають бути обережними з порадами “фінансистів” на цих платформах. Поява інструментів штучного інтелекту, таких як ChatGPT та Bard, ще більше ускладнила перевірку інформації», – зазначив глава CFP Кевін Келлер.

Близько 50% респондентів вірять, що незабаром ШІ зможе доповнити послуги традиційних консультантів. Ще 11% вірять, що нейромережі повністю замінять їх.

Раніше опитування серед керівників служб управління корпоративними ризиками показало, що 66% менеджерів вважають генеративну ШІ загрозою для організацій.

Нагадаємо, за даними дослідження Quartz , штучний інтелект на 15% успішніше справляється з проходженням CAPTCHA, ніж живі люди.

Джерело