Привет, друзья! В 2019 году WebAssembly (далее — WA или wasm) стал четвертым "языком" веба. Первые три — это, разумеется, HTML, CSS и JavaScript. Сегодня wasm поддерживается 94% браузеров. Он, как утверждается, обеспечивает скорость выполнения кода, близкую к нативной (естественной, т.е. максимально возможной для браузера), позволяя портировать в веб десктопные приложения и видеоигры. Что не так с JS? JS — это интерпретируемый язык программирования с динамической типизацией. Динамическая типизация означает, что тип переменной проверяется (определяется) во время выполнения кода. И что с того? — спросите вы. Вот как определяется переменная в C++: Такое определение сообщает компилятору тип переменной n и ее локацию в памяти. И все это в одной строке. А в случае с определением аналогичной переменной в JS (const n = 42), движку сначала приходится определять, что переменная является числом, затем, что число является целым и т.д. при каждом выполнении программы. На определение и (часто) приведение (преобразование) типов каждой инструкции уходит какое-то время. Процесс выполнения кода в JS выглядит примерно так: А в WA так: Это делает WA более производительным, чем JS. В защиту JS можно сказать, что он разрабатывался для придания "легкой" интерактивности веб-страницам, а не для создания высокопроизводительных приложений, выполняющих сложные вычисления. Что такое WA? Формальное определение гласит, что WA — это открытый формат байт-кода, позволяющий переносить код, написанный на таких языках как C, C++, C#, Rust и Go в низкоуровневые ассемблерные инструкции, выполняемые браузером. По сути, это виртуальный микропроцессор, преобразующий высокоуровневый язык в машинный код. На изображении ниже представлен процесс преобразования функции для сложения чисел (add), написанной на C++, в бинарный (двоичный) формат: 
Обратите внимание: WA — это не язык программирования. Это технология (инструмент), позволяющая конвертировать код на указанных выше языках в понятный для браузеров машинный код. Как WA работает? WA — это веб-ассемблер. Но что такое ассемблер? Если очень простыми словами, то На изображении ниже представлен процесс выполнения программы на C на компьютере: 
Пример использования WA Код примера с исходниками. Что нужно сделать, чтобы использовать WA в браузере (или на сервере в Node.js)? И действительно ли WA-код является более производительным, чем JS-код? Давайте это выясним. Предположим, что у нас имеется такая функция на C++: int ... int означает, что функция принимает целое число и возвращает целое число. Как видите, наша функция вычисляет сумму чисел из последовательности Фибоначчи (далее — фибонача :)). Сначала эту функцию необходимо конвертировать в wasm-модуль. Для этого существуют разные способы и инструменты. В нашем случае для этого вполне подойдет WasmExplorer. 
Вставляем код в первую колонку, нажимаем Compile для компиляции кода в Wat(текстовое представление двоичного формата wasm) и Download для преобразования .wat в .wasm и скачивания файла (test.wasm). Переименуем этот файл в fib.wasm. Подготовим проект. Нам потребуется сервер. Зачем? Об этом чуть позже. Структура проекта: Обратите внимание на расширение файла server. Добавляем в package.json команду для запуска сервера для разработки: Код сервера (server.mjs): Сервер, запущенный по адресу http://localhost:5000, возвращает файлы из директории public по запросам клиента, без CORS. Зачем нам сервер? Потому что для загрузки (импорта) wasm-модулей в JS-код используется либо XHR, либо fetch, которые заблокируют получение файла из источника file:// (это связано с безопасностью). Немного забегая вперед, скажу, что для импорта wasm-модулей существует специальное API, предоставляющее несколько методов. Эти методы условно можно разделить на старые и новые. Мы рассмотрим и те, и другие. Суть в том, что при импорте модуля с помощью старых методов можно обойтись расширением для VSCode типа Live Server. Однако новые методы требуют наличия в ответе заголовка Content-Type: application/wasm (судя по всему, express добавляет такой заголовок автоматически на основе названия файла или его содержимого, а расширение нет). Еще один момент: мы рассмотрим только импорт wasm-модуля в JS и использование экспортируемой из экземпляра модуля фибоначи. Однако у нас также имеется возможность передавать функции и переменные в wasm-модуль из JS. Глянем на разметку (index.html): У нас имеется 3 параграфа для вывода результатов функций, а также результатов их сравнения. Мы также подключаем основной скрипт клиента в виде модуля. Перейдем непосредственно к клиентскому скрипту (script.js): Получаем ссылки на DOM-элементы и создаем глобальную (в пределах модуля) переменную для С++-фибоначи. Это старый (условно) способ загрузки wasm-модулей: Функция инициализации переменной fibC: Откуда мы знаем название экспортируемой функции _Z3fibi? Отсюда: 
 JS-фибонача. К слову, на TypeScript это будет выглядеть так: Здесь мы явно указываем, что функция принимает и возвращает числа. Но, во-первых, в этом нет необходимости, поскольку TS в состоянии сам это определить (предположение типов), во-вторых, это не решает проблем JS, о которых говорилось выше. TS — это некий компромисс между статическими и динамическими (с точки зрения типов) языками. Выполним код фибонач: Запускам сервер с помощью команды yarn dev и переходим по адресу http://localhost:5000. 
Отлично, код работает. Но как определить, какой код выполняется быстрее? Легко. Данная функция возвращает время выполнения функции, переданной в качестве аргумента, в мс (округленных в меньшую сторону: ~~ — это сокращение для Math.floor). Перед применением этой функции, перепишем код для загрузки wasm-модуля. Новый способ выглядит следующим образом: Функция loadWasmNew принимает адрес wasm-модуля и название экспортируемой функции. Метод instantiateStreaming принимает промис, возвращаемый вызовом fetch, и возвращает объект, содержащий модуль и экземпляр WA. Модуль можно, например, кешировать и в дальнейшем использовать для создания других экземпляров: 
Мы видим, что C++-фибонача почти в 2 раза (sic) производительнее JS-фибоначи. Получим точные цифры. 
Полагаю, гипотеза о более высокой производительности WA по сравнению с JS подтверждена. Означает ли это, что веб-разработчикам нужно срочно изучать один из языков, компилируемых в WA, с целью написания wasm-модулей и их использования в скриптах? Не думаю. По крайней мере, сейчас ;) Во-первых, экосистема JS содержит огромное количество готовых решений на все случаи жизни. Каждый день появляется что-то новое, в том числе, более производительное. Пройдет немало времени, прежде чем сформируется более-менее серьезная инфраструктура wasm-модулей для веба. Тот, кто организует реестр таких модулей наподобие npm (или внутри него), будет большим молодцом :). Учитесь у Boris Yankov — субреестр можно, например, назвать @wasm. Думаете, почему Deno не "взлетает"? Потому что есть "готовый" Node.js. Или GraphQL? Потому что точечную выборку и обновление данных можно делать и через REST. Про RPC (gRPC) ничего не буду говорить, поскольку не знаком с ним от слова "совсем", но varы и колбеки в Quick Start для Node.js — это несерьезно. Небольшое лирическое отступление. Обратите внимание: это просто мысли вслух, а не приглашение к дискуссии. Но кто знает, что будет завтра? Ситуация может резко измениться, когда появится возможность импортировать wasm-модули напрямую подобно JS-модулям — import { _Z3fibi as fibC } from './fib.wasm'. Или когда WA сможет манипулировать DOM. Во-вторых, работающий медленно JS-код почти всегда можно сделать лучше. На примере той же фибоначи: Кода стало больше, но: 
Результат вычисляется моментально. Пожалуй, это все, чем я хотел поделиться с вами в данной заметке. Основные источники: Парочка инструментов: Благодарю за внимание и хорошего дня!  JavaScript Developer