В JavaScript выполнение кода происходит в однопоточном режиме: главный поток отвечает за обработку интерфейса, событий и выполнения скриптов. Любые тяжёлые вычисления блокируют рендеринг страницы, вызывая задержки в отклике интерфейса. Для решения этой проблемы используется многопоточность через Web Workers — отдельные потоки, выполняющие код параллельно основному потоку.
Web Worker создаётся через конструктор
Worker с указанием файла скрипта:
const worker = new Worker('worker.js');
Внутри воркера можно выполнять любые вычисления, но он не
имеет доступа к DOM и глобальным объектам главного потока. Для
взаимодействия используется система сообщений через
postMessage и обработчик onmessage:
// Внутри воркера
self.onmess age = (event) => {
const result = event.data * 2;
self.postMessage(result);
};
// В главном потоке
worker.onmess age = (event) => {
console.log('Результат из воркера:', event.data);
};
worker.postMessage(10);
Передача сообщений между потоками копирует данные, что важно учитывать при работе с большими структурами, например с состоянием физического движка.
Cannon.js — это библиотека для физического моделирования на JavaScript, которая часто используется для симуляции твёрдотельной динамики, столкновений и гравитации. Основная нагрузка в Cannon.js приходится на расчёт физических взаимодействий, что делает её идеальной для переноса вычислений в воркер.
Пример создания физического мира внутри воркера:
importScripts('cannon.min.js');
const world = new CANNON.World();
world.gravity.set(0, -9.82, 0);
const sphereBody = new CANNON.Body({
mass: 5,
shape: new CANNON.Sphere(1)
});
sphereBody.position.set(0, 10, 0);
world.addBody(sphereBody);
self.onmess age = (event) => {
if (event.data.type === 'step') {
world.step(1 / 60);
self.postMessage({
positions: world.bodies.map(body => body.position)
});
}
};
В главном потоке можно визуализировать результат, полученный из воркера:
worker.onmess age = (event) => {
const positions = event.data.positions;
positions.forEach((pos, idx) => {
meshArray[idx].position.set(pos.x, pos.y, pos.z);
});
};
function animate() {
worker.postMessage({ type: 'step' });
requestAnimationFrame(animate);
}
animate();
Ключевая проблема при работе с воркерами —
эффективная передача состояния между потоками. Простая передача объектов
через postMessage создаёт глубокую копию, что может
замедлять работу при больших массивах тел или сложных сценах.
Cannon.js позволяет использовать массивы чисел
(Float32Array, ArrayBuffer) для хранения
позиции, ориентации и скоростей тел. Эти массивы можно передавать с
помощью механизма transferable objects, который
перемещает данные, а не копирует их:
const positions = new Float32Array(world.bodies.length * 3);
self.postMessage({ positions }, [positions.buffer]);
После передачи в главном потоке массив можно напрямую использовать для обновления графики без лишнего копирования.
Для ещё более быстрой синхронизации можно применять SharedArrayBuffer, который позволяет двум потокам работать с одним и тем же массивом в памяти:
// Главный поток
const sharedBuffer = new SharedArrayBuffer(Float32Array.BYTES_PER_ELEMENT * bodiesCount * 3);
const sharedPositions = new Float32Array(sharedBuffer);
worker.postMessage({ sharedPositions, type: 'init' });
// В воркере
self.onmess age = (event) => {
if(event.data.type === 'init') {
positions = new Float32Array(event.data.sharedPositions);
}
};
function updateWorld() {
world.step(1/60);
world.bodies.forEach((body, idx) => {
positions[idx*3] = body.position.x;
positions[idx*3 + 1] = body.position.y;
positions[idx*3 + 2] = body.position.z;
});
}
Преимущество SharedArrayBuffer — отсутствие копирования и минимальная задержка между потоками, что критично для высокопроизводительных симуляций.
world.step(dt).Рекомендации по производительности:
Многопоточность через Web Workers значительно увеличивает производительность Cannon.js, особенно для больших сцен с сотнями тел и сложными столкновениями. Правильная организация передачи состояния, использование Typed Arrays и SharedArrayBuffer позволяют достичь плавного и отзывчивого взаимодействия графики и физики.