WASM 및 웹 작업자를 사용하여 브라우저에서 Go 실행



게시물Running Go in the Browser with WASM and Web WorkersQvault에 처음 등장했습니다.

최근 Qvault의 브라우저에서 Go를 실행하는 방법을 크게 변경했으며 향상된 기능에 대해 설명하고자 합니다. 웹 작업자는 우리를 방해했던 심각한 브라우저 관련 코딩 문제를 해결할 수 있었던 이유입니다. 이 기사는 Running Go in the Browser with Web Assembly의 속편이라고 생각하십시오.

최신 과정Big-O Algorithms을 게시하는 동안 코드가 계속 실행되는 동안 콘솔 출력을 인쇄하는 방법이 필요했습니다. 브라우저에서 계산 비용이 많이 드는 알고리즘을 실행할 때 문제가 발생했습니다. 브라우저가 너무 느려서 새로운 출력 라인을 렌더링할 수 없습니다. 우리는 웹 작업자를 구현하기로 결정했고 그들은 문제를 쉽게 해결했습니다.

문제



이전 Qvault에서는 콘솔 출력이 모두 한 번에 인쇄되었습니다. 프로그램이 실행된 다음 출력이 표시되었습니다. 특히 속도를 위해 알고리즘을 최적화하려고 할 때 무언가가 인쇄될 때 확인하는 것이 종종 유용하기 때문에 이것이 이상적이지 않다는 것을 알았습니다.

예를 들어 다음 코드는 모든 출력을 한 번에 인쇄하는 데 사용됩니다.

package main

import (
    "fmt"
)

func main(){
    const max = 100000000
    for i := 0; i < max; i++{
        if i % (max/10) == 0{
            fmt.Println(i)
        }
    }
}



웹 작업자를 추가한 이후로 이제 실행 시 각 숫자를 적절하게 인쇄합니다. playground here에서 직접 확인할 수 있습니다.

웹 작업자란 무엇입니까?



Web Workers are a simple means for web content to run scripts in background threads.

Mozilla



즉, JavaScript의 단일 스레드 클러치에서 마침내 자유로울 수 있는 방법입니다! 비용이 많이 드는 작업을 다른 실행 스레드로 오프로드할 수 있습니다. 이렇게 하면 브라우저가 자유롭게 화면에 업데이트를 렌더링할 수 있습니다.

작동 방식 – 작업자



아시다시피 저희는 편집기의 코드를 저희 서버의 WASM으로 컴파일합니다. 그 부분이 궁금하시다면 저희previous post.에서 읽어보실 수 있습니다. 코드가 웹 어셈블리로 컴파일되면 실행을 위해 프런트 엔드로 다시 배송됩니다.

Web Worker를 실행하려면 작업자를 정의하는 스크립트가 필요합니다. 자바스크립트 파일일 뿐입니다.

addEventListener('message', async (e) => {
    // initialize the Go WASM glue
    const go = new self.Go();

    // e.data contains the code from the main thread
    const result = await WebAssembly.instantiate(e.data, go.importObject);

    // hijack the console.log function to capture stdout
    let oldLog = console.log;
    // send each line of output to the main thread
    console.log = (line) => { postMessage({
        message: line
    }); };

    // run the code
    await go.run(result.instance);
    console.log = oldLog;

    // tell the main thread we are done
    postMessage({
        done: true
    });
}, false);



작업자는 message 이벤트를 수신하고 postMessage 함수를 통해 데이터를 다시 전송하여 기본 스레드와 통신합니다.

참고: 작업자가 Go 코드를 실행하는 데 필요한 wasm_exec.js 파일을 생략했지만 Go가 설치되어 있으면 컴퓨터에서 찾을 수 있습니다.

cat $(go env GOROOT)/misc/wasm/wasm_exec.js


작동 방식 – 메인 스레드



이제 컴파일된 웹 어셈블리를 실행할 수 있는 작업자 파일이 있으므로 메인 스레드가 작업자와 통신하는 방법을 살펴보겠습니다. 일부 도우미 기능을 내보내는 ES6 모듈을 만들었습니다.

export function getWorker(lang) {
  return {
    webWorker: new window.Worker(`/${lang}_worker.js`),
    lang
  };
}

export function useWorker(worker, params, callback) {
  const promise = new Promise((resolve, reject) => {
    worker.webWorker.onmessage = (event) => {
      if (event.data.done) {
        resolve();
        return;
      }
      if (event.data.error) {
        reject(event.data.error);
        return;
      }
      callback(event.data.message);
    };
  });
  worker.webWorker.postMessage(params);
  return promise;
}

export function terminateWorker(worker) {
  worker.webWorker.terminate();
}


페이지가 로드되면 getWorker를 사용하여 새 웹 작업자를 만듭니다. 사용자가 일부 코드를 실행하면 useWorker를 사용하여 작업자에게 코드를 보냅니다. 코드 편집기에서 벗어날 때 terminateWorker 를 사용하여 작업자를 정리할 수 있습니다.
useWorker 함수는 게시물에서 흥미로운 부분입니다. getWorker로 생성된 작업자, 작업자에게 전달될 params라는 객체(컴파일된 WASM 포함) 및 작업자가 작업을 완료할 때 실행할 콜백 함수를 사용합니다.

예를 들어 Vue 앱에서는 다음과 같이 이러한 기능을 사용합니다.

this.output = [];
this.isLoading = true;
const wasm = await compileGo(this.code);
await useWorker(this.worker, wasm, (data) => {
  this.output.push(data); 
});
this.isLoading = false;


그리고 this.output는 Vue 인스턴스의 반응형 속성이므로 Web Worker에서 데이터를 받을 때마다 새 출력이 콘솔에 출력됩니다.

읽어 주셔서 감사합니다!



질문이나 의견이 있으면 Twitter에서 팔로우하세요.

좀 가져가 coding courses on our new platform

Subscribe 더 많은 프로그래밍 기사를 보려면 뉴스레터로

좋은 웹페이지 즐겨찾기