문자열에서 웹 작업자를 만드는 방법


84

POST 요청을 통해 제공되는 문자열에서 웹 워커 생성을 어떻게 사용할 수 있습니까?

제가 생각할 수있는 한 가지 방법은 구현 방법을 잘 모르겠습니다. 서버 응답에서 데이터 URI를 생성하고이를 Worker 생성자에 전달하는 것입니다.하지만 일부 브라우저에서는이를 허용하지 않는다고 들었습니다. 이는 동일한 출처 정책 때문입니다.

MDN은 데이터 URI에 대한 출처 정책에 대한 불확실성을 명시합니다 .

참고 : 작업자 생성자의 매개 변수로 전달 된 URI는 동일 출처 정책을 따라야합니다. 현재 데이터 URI가 동일한 출처인지 아닌지에 대해 브라우저 공급 업체간에 의견 차이가 있습니다. Gecko 10.0 (Firefox 10.0 / Thunderbird 10.0) 이상에서는 작업자를위한 유효한 스크립트로 데이터 URI를 허용합니다. 다른 브라우저는 동의하지 않을 수 있습니다.

whatwg에 대해 논의 하는 게시물도 있습니다 .


CORS ( w3.org/TR/cors )가 도움이 될지 궁금합니다 . HTMl5rocks는 작업자에 대한 동일한 출처 정책 ( html5rocks.com/en/tutorials/workers/basics )에 대해 강력한 "필수"언어를 사용 하므로 CORS는 여기에서별로 도움이되지 않습니다. 그래도 해봤 어?
Pavel Veller 2012

답변:


146

요약

  • blob: Chrome 8 이상, Firefox 6 이상, Safari 6.0 이상, Opera 15 이상
  • data:application/javascript Opera 10.60-12 용
  • eval 그렇지 않으면 (IE 10+)

URL.createObjectURL(<Blob blob>)문자열에서 웹 작업자를 만드는 데 사용할 수 있습니다. 더 이상 사용되지 않는BlobBuilder API 또는 생성자를 사용하여 blob을 만들 수 있습니다 .Blob

데모 : http://jsfiddle.net/uqcFM/49/

// URL.createObjectURL
window.URL = window.URL || window.webkitURL;

// "Server response", used in all examples
var response = "self.onmessage=function(e){postMessage('Worker: '+e.data);}";

var blob;
try {
    blob = new Blob([response], {type: 'application/javascript'});
} catch (e) { // Backwards-compatibility
    window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder;
    blob = new BlobBuilder();
    blob.append(response);
    blob = blob.getBlob();
}
var worker = new Worker(URL.createObjectURL(blob));

// Test, used in all examples:
worker.onmessage = function(e) {
    alert('Response: ' + e.data);
};
worker.postMessage('Test');

적합성

웹 작업자는 다음 브라우저 소스 에서 지원됩니다 .

  • 크롬 3
  • Firefox 3.5
  • IE 10
  • 오페라 10.60
  • Safari 4

이 메서드의 지원은 BlobAPI 및 메서드의 지원을 기반으로합니다 URL.createObjectUrl. Blob호환성 :

  • Chrome 8 이상 ( WebKitBlobBuilder), 20 이상 ( Blob생성자)
  • Firefox 6 이상 ( MozBlobBuilder), 13 이상 ( Blob생성자)
  • Safari 6+ ( Blob생성자)

IE10은 MSBlobBuilderURL.createObjectURL. 그러나 blob:-URL 에서 Web Worker를 만들려고 하면 SecurityError가 발생합니다.

Opera 12는 URLAPI를 지원하지 않습니다 . 이 해킹URL 덕분에 일부 사용자는 개체 의 가짜 버전을 가질 수 있습니다 .browser.js

폴백 1 : 데이터 URI

Opera는 Worker생성자에 대한 인수로 데이터 URI를 지원합니다 . 참고 : 특수 문자 (예 : #%) 를 이스케이프하는 것을 잊지 마십시오 .

// response as defined in the first example
var worker = new Worker('data:application/javascript,' +
                        encodeURIComponent(response) );
// ... Test as defined in the first example

데모 : http://jsfiddle.net/uqcFM/37/

폴백 2 : 평가

eval Safari (<6) 및 IE 10의 대체 수단으로 사용할 수 있습니다.

// Worker-helper.js
self.onmessage = function(e) {
    self.onmessage = null; // Clean-up
    eval(e.data);
};
// Usage:
var worker = new Worker('Worker-helper.js');
// `response` as defined in the first example
worker.postMessage(response);
// .. Test as defined in the first example

3
@BrianFreid 편집 해 주셔서 감사합니다.하지만 필요하지 않습니다. 당신이 더 몇 줄을 보면, "IE10 지원 볼 수 MSBlobBuilder등을 URL.createObjectURL. 그러나,에서 웹 노동자를 만들려고 blob:-URL이 오류 SecurityError.". 따라서 추가 MSBlobBuilder는 효과가 없으며 유일한 옵션은 대체 # 2입니다.
Rob W

Opera 12는 더 이상 정의하지 않으며 URL(따라서 속성도 정의하지 않음) Blob 생성자는 현재 충분히 지원됩니다.
gsnedders 2013 년

2
나는 이것이 적어도 미리보기에서 IE11에서 여전히 발생하는지 확인했습니다.
Benjamin Gruenbaum

1
dataURI는 Opera 또는 기타 모든 브라우저 (IE 제외)에서만 지원됩니까?
jayarjo

1
@jayarjo data:-URI for Web Workers는 Firefox에서도 지원되지만 Chrome 또는 Opera 15 이상에서는 지원되지 않습니다. 의 성능은 eval관련이 없으며 초당 수백만 명의 웹 작업자를 생성하지 않을 것입니다.
Rob W

12

현재 허용되는 답변에 동의하지만 작업자 코드를 문자열 형식으로 편집하고 관리하는 것이 종종 바쁠 것입니다.

따라서 선택적으로 작업자를 함수로 유지 한 다음 string-> blob으로 숨길 수있는 아래 접근 방식을 사용할 수 있습니다.

// function to be your worker
function workerFunction() {
    var self = this;
    self.onmessage = function(e) {
        console.log('Received input: ', e.data); // message received from main thread
        self.postMessage("Response back to main thread");
    }
}


///////////////////////////////

var dataObj = '(' + workerFunction + ')();'; // here is the trick to convert the above fucntion to string
var blob = new Blob([dataObj.replace('"use strict";', '')]); // firefox adds "use strict"; to any function which might block worker execution so knock it off

var blobURL = (window.URL ? URL : webkitURL).createObjectURL(blob, {
    type: 'application/javascript; charset=utf-8'
});


var worker = new Worker(blobURL); // spawn new worker

worker.onmessage = function(e) {
    console.log('Worker said: ', e.data); // message received from worker
};
worker.postMessage("some input to worker"); // Send data to our worker.

이것은 IE11 + 및 FF 및 Chrome에서 테스트되었습니다.


1
@SenJacob 이것은 커뮤니티 위키 게시물이 아니므로 편집 대신 댓글을 통해 잠재적 인 문제를 포스터에 노출해야합니다.
Goodbye StackExchange

@FrankerZ 죄송합니다. 내가 한 변경 사항으로 IE11에서 작동하도록 만들어야했습니다. @ ChanuSukarno 개정 3 의 변경 사항이 괜찮은지 확인해 주 시겠습니까?
Sen Jacob

참고로 "type : 'application / javascript; charset = utf-8'"은 createObjectURL 호출이 아니라 Blob 생성자에 속합니다.
Sora2455

그래서 ... 단순히 텍스트 편집기에서 더 잘 읽기 위해 작업자 외부에서 함수를 구축하고 있습니까? 말도 안 돼. 이유없이 두 컨텍스트에서 해당 함수를 메모리에로드해야합니다.
ADJenks

4

대부분의 아이디어로 접근하고 내 아이디어를 추가했습니다. 내 코드가 작업자에게 필요한 유일한 것은 'self'범위를 참조하기 위해 'this'를 사용하는 것입니다. 나는 이것이 매우 즉흥적이라고 확신합니다.

// Sample code
var code = function() {
    this.onmessage = function(e) {
        this.postMessage('Worker: '+e.data);
        this.postMessage('Worker2: '+e.data);
    };
};

// New thread worker code
FakeWorkerCode = function(code, worker) {
    code.call(this);
    this.worker = worker;
}
FakeWorkerCode.prototype.postMessage = function(e) {
    this.worker.onmessage({data: e});
}
// Main thread worker side
FakeWorker = function(code) {
    this.code = new FakeWorkerCode(code, this);
}
FakeWorker.prototype.postMessage = function(e) {
    this.code.onmessage({data: e});
}

// Utilities for generating workers
Utils = {
    stringifyFunction: function(func) {
        // Stringify the code
        return '(' + func + ').call(self);';
    },
    generateWorker: function(code) {
        // URL.createObjectURL
        windowURL = window.URL || window.webkitURL;   
        var blob, worker;
        var stringified = Utils.stringifyFunction(code);
        try {
            blob = new Blob([stringified], {type: 'application/javascript'});
        } catch (e) { // Backwards-compatibility
            window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder;
            blob = new BlobBuilder();
            blob.append(stringified);
            blob = blob.getBlob();
        }

        if ("Worker" in window) {
            worker = new Worker(windowURL.createObjectURL(blob));
        } else {
            worker = new FakeWorker(code);
        }
        return worker;
    }
};

// Generate worker
var worker = Utils.generateWorker(code);
// Test, used in all examples:
worker.onmessage = function(e) {
    alert('Response: ' + e.data);
};
function runWorker() {
    worker.postMessage('working fine');
}

데모 : http://jsfiddle.net/8N6aR/


4

허용되는 대답은 이전 버전과의 호환성을 지원하기 때문에 약간 복잡하므로 동일한 내용을 게시하고 싶었지만 단순화했습니다. (최신) 브라우저 콘솔에서 다음을 시도하십시오.

const code = "console.log('Hello from web worker!')"
const blob = new Blob([code], {type: 'application/javascript'})
const worker = new Worker(URL.createObjectURL(blob))
// See the output in your console.


2

좋은 대답-저는 오늘 폴백 기능이있는 웹 워커를 사용할 수 없을 때 (예 : 메인 스레드에서 워커 스크립트를 실행) 만들려고 할 때 비슷한 문제를 해결했습니다. 이 스레드는 주제와 관련이 있으므로 여기에 솔루션을 제공 할 것이라고 생각했습니다.

    <script type="javascript/worker">
        //WORKER FUNCTIONS
        self.onmessage = function(event) {
            postMessage('Hello, ' + event.data.name + '!');
        }
    </script>

    <script type="text/javascript">

        function inlineWorker(parts, params, callback) {

            var URL = (window.URL || window.webkitURL);

            if (!URL && window.Worker) {

                var worker = new window.Worker(URL.createObjectURL(new Blob([parts], { "type" : "text/javascript" })));

                worker.onmessage = function(event) {
                  callback(event.data);
                };

                worker.postMessage(params);

            } else {

                var postMessage = function(result) {
                  callback(result);
                };

                var self = {}; //'self' in scope of inlineWorker. 
                eval(parts); //Converts self.onmessage function string to function on self via nearest scope (previous line) - please email chrisgwgreen.site@gmail.com if this could be tidier.
                self.onmessage({ 
                    data: params 
                });
            }
        }

        inlineWorker(
            document.querySelector('[type="javascript/worker"]').textContent, 
            {
                name: 'Chaps!!'
            },
            function(result) {
                document.body.innerHTML = result;
            }
        );

    </script>
</body>


1

사용 사례에 따라 다음과 같은 것을 사용할 수 있습니다.

task.js 모든 코어 (node.js 및 웹)에서 실행되는 CPU 집약적 코드를 얻기위한 단순화 된 인터페이스

예는

// turn blocking pure function into a worker task
const functionFromPostRequest = task.wrap('function (exampleArgument) {}');

// run task on a autoscaling worker pool
functionFromPostRequest('exampleArgumentValue').then(result => {
    // do something with result
});

1

@Chanu_Sukarno의 코드를 확장하면 작업자 함수 (또는 문자열)를이 함수에 전달하기 만하면 웹 작업자 내에서 실행됩니다.

async function doWorkerTask(workerFunction, input, buffers) {
  // Create worker
  let fnString = '(' + workerFunction.toString().replace('"use strict";', '') + ')();';
  let workerBlob = new Blob([fnString]);
  let workerBlobURL = window.URL.createObjectURL(workerBlob, { type: 'application/javascript; charset=utf-8' });
  let worker = new Worker(workerBlobURL);

  // Run worker
  return await new Promise(function(resolve, reject) {
    worker.onmessage = function(e) { resolve(e.data); };
    worker.postMessage(input, buffers);
  });
}

다음은 사용 방법의 예입니다.

function myTask() {
  self.onmessage = function(e) {
    // do stuff with `e.data`, then:
    self.postMessage("my response");
    self.close();
  }
}
let output = await doWorkerTask(myTask, input, inputBuffers);
// now you can do something with `output` (which will be equal to "my response")


에서 nodejs , doWorkerTask다음과 같습니다 :

async function doWorkerTask(workerFunction, input, buffers) {
  let Worker = require('webworker-threads').Worker;
  let worker = new Worker(workerFunction);

  // Run worker
  return await new Promise(function(resolve, reject) {
    worker.onmessage = function(e) { resolve(e.data); };
    worker.postMessage(input, buffers);
  });
}

-1

를 또는 로 변경하여 blob뿐만 아니라 objectURL 에서 실제 데이터를 가져올 수 있습니다 .responseType"text""arraybuffer"

여기서 A는 전후 변환text/javascriptblobobjectURL다시 blob하거나 text/javascript.

궁금한 점이 있다면 외부 파일없이 웹 작업자 를 생성하는 데
사용하고 있습니다. 바이너리 콘텐츠를 반환하는 데 사용할 수 있습니다 (예 : YouTube 비디오;) (<video> 태그 리소스 속성에서)

var blob = new Blob(['self.onmessage=function(e){postMessage(e)}'],{type: 'text/javascript'});   //->console: (object)   Blob {size: 42, type: "text/javascript", slice: function}

var obju = URL.createObjectURL(js_blob); //->console:  "blob:http%3A//stackoverflow.com/02e79c2b-025a-4293-be0f-f121dd57ccf7"

var xhr = new XMLHttpRequest();
xhr.open('GET', 'blob:http%3A//stackoverflow.com/02e79c2b-025a-4293-be0f-f121dd57ccf7', true);
xhr.responseType = 'text'; /* or "blob" */
xhr.onreadystatechange = function(){
  if(xhr.DONE !== xhr.readyState) return;

  console.log(xhr.response);
}
xhr.send();

/*
  responseType "blob" ->console: (object)   Blob {size: 42, type: "text/javascript", slice: function}
  responseType "text" ->console: (text)     'self.onmessage=function(e){postMessage(e)}'
*/

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.