별도의 자바 스크립트 파일이없는 웹 작업자?
내가 알 수있는 한 웹 작업자는 별도의 JavaScript 파일로 작성하고 다음과 같이 호출해야합니다.
new Worker('longrunning.js')
클로저 컴파일러를 사용하여 모든 JavaScript 소스 코드를 결합하고 최소화하고 있으며 배포를 위해 작업자를 별도의 파일로 만들 필요가 없습니다. 이것을 할 수있는 방법이 있습니까?
new Worker(function() {
//Long-running work here
});
일류 함수가 JavaScript에 매우 중요하기 때문에 백그라운드 작업을 수행하는 표준 방법이 웹 서버에서 다른 JavaScript 파일을 모두로드해야하는 이유는 무엇입니까?
http://www.html5rocks.com/en/tutorials/workers/basics/#toc-inlineworkers
작업자 스크립트를 즉석에서 만들거나 별도의 작업자 파일을 만들지 않고 자체 포함 된 페이지를 만들려면 어떻게해야합니까? Blob ()을 사용하면 작업자 코드에 대한 URL 핸들을 문자열로 만들어 기본 논리와 동일한 HTML 파일에서 작업자를 "인라인"할 수 있습니다
BLOB 인라인 작업자의 전체 예 :
<!DOCTYPE html>
<script id="worker1" type="javascript/worker">
// This script won't be parsed by JS engines because its type is javascript/worker.
self.onmessage = function(e) {
self.postMessage('msg from worker');
};
// Rest of your worker code goes here.
</script>
<script>
var blob = new Blob([
document.querySelector('#worker1').textContent
], { type: "text/javascript" })
// Note: window.webkitURL.createObjectURL() in Chrome 10+.
var worker = new Worker(window.URL.createObjectURL(blob));
worker.onmessage = function(e) {
console.log("Received: " + e.data);
}
worker.postMessage("hello"); // Start the worker.
</script>
HTML에 웹 워커 코드를 포함시키는 html5rocks 솔루션은 상당히 끔찍합니다.
그리고 이스케이프 된 JavaScript-as-a-string 문자열은 작업 흐름을 복잡하게하기 때문에 낫지 않습니다 (클로저 컴파일러는 문자열에서 작동 할 수 없음).
개인적으로 나는 toString 메소드를 정말로 좋아하지만 @ dan-man 정규식!
내가 선호하는 접근법 :
// Build a worker from an anonymous function body
var blobURL = URL.createObjectURL( new Blob([ '(',
function(){
//Long-running work here
}.toString(),
')()' ], { type: 'application/javascript' } ) ),
worker = new Worker( blobURL );
// Won't be needing this anymore
URL.revokeObjectURL( blobURL );
지원은이 세 테이블의 교차점입니다.
- http://caniuse.com/#feat=webworkers
- http://caniuse.com/#feat=blobbuilder
- http://caniuse.com/#feat=bloburls
그러나 선택적 'name'매개 변수가 일치하더라도 URL이 정확히 일치해야하므로 SharedWorker 에서는 작동하지 않습니다 . SharedWorker의 경우 별도의 JavaScript 파일이 필요합니다.
2015 년 업데이트-ServiceWorker 특이점 도착
이제이 문제를 해결하는 훨씬 더 강력한 방법이 있습니다. 다시 작업자 코드를 정적 문자열이 아닌 함수로 저장하고 .toString ()을 사용하여 변환 한 다음 선택한 정적 URL 아래의 CacheStorage에 코드를 삽입하십시오.
// Post code from window to ServiceWorker...
navigator.serviceWorker.controller.postMessage(
[ '/my_workers/worker1.js', '(' + workerFunction1.toString() + ')()' ]
);
// Insert via ServiceWorker.onmessage. Or directly once window.caches is exposed
caches.open( 'myCache' ).then( function( cache )
{
cache.put( '/my_workers/worker1.js',
new Response( workerScript, { headers: {'content-type':'application/javascript'}})
);
});
가능한 두 가지 대체가 있습니다. 위와 같이 또는 더 매끄럽게 ObjectURL은 /my_workers/worker1.js에 실제 JavaScript 파일을 넣습니다.
이 방법의 장점은 다음과 같습니다.
- SharedWorkers도 지원할 수 있습니다.
- 탭은 고정 주소에서 단일 캐시 사본을 공유 할 수 있습니다. Blob 방식은 모든 탭에 대해 임의의 objectURL을 확산시킵니다.
실행 컨텍스트를 인식하고 상위 스크립트와 작업자 역할을 모두 수행 할 수있는 단일 JavaScript 파일을 작성할 수 있습니다. 다음과 같은 파일의 기본 구조부터 시작하겠습니다.
(function(global) {
var is_worker = !this.document;
var script_path = is_worker ? null : (function() {
// append random number and time to ID
var id = (Math.random()+''+(+new Date)).substring(2);
document.write('<script id="wts' + id + '"></script>');
return document.getElementById('wts' + id).
previousSibling.src;
})();
function msg_parent(e) {
// event handler for parent -> worker messages
}
function msg_worker(e) {
// event handler for worker -> parent messages
}
function new_worker() {
var w = new Worker(script_path);
w.addEventListener('message', msg_worker, false);
return w;
}
if (is_worker)
global.addEventListener('message', msg_parent, false);
// put the rest of your library here
// to spawn a worker, use new_worker()
})(this);
보시다시피, 스크립트에는 부모와 작업자의 관점 모두에 대한 모든 코드가 포함되어 있으며 자체 인스턴스가 인 작업자인지 확인합니다 !document
. script_path
제공되는 경로 new Worker
가 스크립트가 아닌 상위 페이지를 기준으로하기 때문에 다소 다루기 어려운 계산이 상위 페이지를 기준으로 스크립트의 경로를 정확하게 계산하는 데 사용됩니다 .
이 Blob
방법을 사용하면 작업자 팩토리의 경우는 어떻습니까?
var BuildWorker = function(foo){
var str = foo.toString()
.match(/^\s*function\s*\(\s*\)\s*\{(([\s\S](?!\}$))*[\s\S])/)[1];
return new Worker(window.URL.createObjectURL(
new Blob([str],{type:'text/javascript'})));
}
그래서 당신은 이것을 이렇게 사용할 수 있습니다 ...
var myWorker = BuildWorker(function(){
//first line of worker
self.onmessage(){....};
//last line of worker
});
편집하다:
교차 스레드 통신을보다 쉽게 수행 할 수 있도록이 아이디어를 더 확장했습니다 : bridged-worker.js .
편집 2 :
위의 링크는 내가 만든 요점에 대한 것입니다. 다른 누군가가 나중에 그것을 실제 저장소 로 바 꾸었습니다 .
웹 워커는 개별 프로그램과는 완전히 다른 맥락에서 작동합니다.
이는 코드가 다른 컨텍스트에 속하는 클로저를 통해 개체를 참조 할 수 있기 때문에 한 컨텍스트에서 다른 컨텍스트로 코드를 이동할 수 없음을 의미합니다.
이는 ECMAScript가 단일 스레드 언어로 설계 되었기 때문에 특히 중요하며 웹 작업자는 별도의 스레드에서 작동하므로 스레드로부터 안전하지 않은 작업이 수행 될 위험이 있습니다.
이것은 다시 웹 워커가 소스 형식의 코드로 초기화되어야 함을 의미합니다.
WHATWG 의 사양에 따르면
결과 절대 URL의 원점이 입력 스크립트의 원점과 동일하지 않은 경우 SECURITY_ERR 예외를 발생시킵니다.
따라서 스크립트는 원본 페이지와 동일한 체계를 가진 외부 파일이어야합니다. 데이터 : URL 또는 javascript : URL에서 스크립트를로드 할 수 없으며 https : 페이지에서 http : URL이있는 스크립트를 사용하여 작업자를 시작할 수 없습니다.
그러나 불행히도 소스 코드가있는 문자열을 생성자에 전달할 수 없었던 이유를 실제로 설명하지 못합니다.
인라인 작업자를위한 더 나은 방법 읽기
var worker_fn = function(e)
{
self.postMessage('msg from worker');
};
var blob = new Blob(["onmessage ="+worker_fn.toString()], { type: "text/javascript" });
var worker = new Worker(window.URL.createObjectURL(blob));
worker.onmessage = function(e)
{
alert(e.data);
};
worker.postMessage("start");
최근 답변 (2018)
Greenlet 을 사용할 수 있습니다 :
비동기 함수를 자체 스레드로 이동하십시오. Workerize 의 단순화 된 단일 기능 버전 .
예:
import greenlet from 'greenlet'
const getName = greenlet(async username => {
const url = `https://api.github.com/users/${username}`
const res = await fetch(url)
const profile = await res.json()
return profile.name
})
console.log(await getName('developit'))
Adria의 응답을 취하여 현재 Chrome 및 FF에서는 작동하지만 IE10에서는 작동하지 않는 복사 가능한 복사 가능 기능에 넣습니다 (blob의 작업자는 보안 오류 발생 ).
var newWorker = function (funcObj) {
// Build a worker from an anonymous function body
var blobURL = URL.createObjectURL(new Blob(
['(', funcObj.toString(), ')()'],
{type: 'application/javascript'}
));
var worker = new Worker(blobURL);
// Won't be needing this anymore
URL.revokeObjectURL(blobURL);
return worker;
}
다음은 실제 예제입니다. http://jsfiddle.net/ubershmekel/YYzvr/
vkThread 플러그인을 살펴보십시오. htis 플러그인을 사용하면 기본 코드에서 모든 기능을 수행하여 스레드 (웹 워커)에서 실행할 수 있습니다. 따라서 특별한 "웹 작업자 파일"을 만들 필요가 없습니다.
http://www.eslinstructor.net/vkthread/
--Vadim
사용 사례에 따라 다음과 같은 것을 사용할 수 있습니다.
task.js 모든 코어 (node.js 및 웹)에서 CPU 집약적 코드를 실행하기위한 단순화 된 인터페이스
예를 들면
function blocking (exampleArgument) {
// block thread
}
// turn blocking pure function into a worker task
const blockingAsync = task.wrap(blocking);
// run task on a autoscaling worker pool
blockingAsync('exampleArgumentValue').then(result => {
// do something with result
});
인라인 웹 워커를 사용하여 동일한 자바 스크립트에서 웹 워커를 사용할 수 있습니다.
아래 기사는 웹 워커와 웹 워커의 한계 및 디버깅을 쉽게 이해하는 데 도움이됩니다.
이 작업을 수행하는 더 좋은 방법은 Blob 개체를 사용하는 것입니다. 아래에서 간단한 예를 볼 수 있습니다.
// create a Blob object with a worker code
var blob = new Blob(["onmessage = function(e) { postMessage('msg from worker'); }"]);
// Obtain a blob URL reference to our worker 'file'.
var blobURL = window.URL.createObjectURL(blob);
// create a Worker
var worker = new Worker(blobURL);
worker.onmessage = function(e) {
console.log(e.data);
};
worker.postMessage("Send some Data");
jThread를 사용해보십시오. https://github.com/cheprasov/jThread
// You can use simple calling like this
jThread(
function(arr){
//... some code for Worker
return arr;
}
,function(arr){
//... done code
}
)( [1,2,3,4,5,6,7] ); // some params
여기 콘솔 :
var worker=new Worker(window.URL.createObjectURL(new Blob([function(){
//Long-running work here
postMessage('done');
}.toString().split('\n').slice(1,-1).join('\n')],{type:'text/javascript'})));
worker.addEventListener('message',function(event){
console.log(event.data);
});
https://developer.mozilla.org/es/docs/Web/Guide/Performance/Using_web_workers
// Syntax: asyncEval(code[, listener])
var asyncEval = (function () {
var aListeners = [], oParser = new Worker("data:text/javascript;charset=US-ASCII,onmessage%20%3D%20function%20%28oEvent%29%20%7B%0A%09postMessage%28%7B%0A%09%09%22id%22%3A%20oEvent.data.id%2C%0A%09%09%22evaluated%22%3A%20eval%28oEvent.data.code%29%0A%09%7D%29%3B%0A%7D");
oParser.onmessage = function (oEvent) {
if (aListeners[oEvent.data.id]) { aListeners[oEvent.data.id](oEvent.data.evaluated); }
delete aListeners[oEvent.data.id];
};
return function (sCode, fListener) {
aListeners.push(fListener || null);
oParser.postMessage({
"id": aListeners.length - 1,
"code": sCode
});
};
})();
내 작은 플러그인 사용 https://github.com/zevero/worker-create
var worker_url = Worker.createURL(function(e){
self.postMessage('Example post from Worker'); //your code here
});
var worker = new Worker(worker_url);
ES6의 템플릿 리터럴 덕분에 이제 또 다른 멋진 옵션이 있다고 생각합니다. 이를 통해 추가 작업자 기능 (및 이상한 범위)을 생략하고 텍스트를 저장하는 데 사용하는 것과 매우 유사하지만 실제로 문서 또는 DOM이 필요없는 여러 줄 텍스트로 작업자를위한 코드를 작성할 수 있습니다 예를 들면 다음과 같습니다. 예 :
const workerScript = `
self.addEventListener('message', function(e) {
var data = e.data;
console.log('worker recieved: ',data);
self.postMessage('worker added! :'+ addOne(data.value));
self.close();//kills the worker
}, false);
`;
우리는 배열에 그것들을 모아서 .toString을 실행하여 그것들을 문자열로 줄임으로써 (함수 선언이라면 작동해야 함) 작업자에게 원하는 추가 함수 종속성을 가져올 수 있습니다. 그런 다음 스크립트 문자열 앞에 추가하십시오. 그렇게하면 이미 작성중인 코드의 범위에 이미 포함되어있는 스크립트를 가져올 필요가 없습니다.
이 특정 버전의 유일한 단점은 linter가 서비스 작업자 코드를 보풀 수 없다는 것입니다 (문자열이기 때문에) "별도의 작업자 기능 접근"의 이점입니다.
이것은 위의 추가 사항입니다-jsFiddle에서 웹 작업자를 테스트하기위한 멋진 템플릿이 있습니다. Blob 대신 jsFiddles ?js
API를 사용합니다 .
function workerFN() {
self.onmessage = function(e) {
switch(e.data.name) {
case "" :
break;
default:
console.error("Unknown message:", e.data.name);
}
}
}
// This is a trick to generate real worker script that is loaded from server
var url = "/echo/js/?js="+encodeURIComponent("("+workerFN.toString()+")()");
var worker = new Worker(url);
worker.addEventListener("message", function(e) {
switch(e.data.name) {
case "" :
break;
default:
console.error("Unknown message:", e.data.name);
}
})
일반 웹 작업자 및 공유 작업자 템플릿을 사용할 수 있습니다.
CodePen이 현재 (또는 속성이없는) 인라인 <script>
태그를 구문 강조 표시하지 않는다는 것을 발견했습니다 type="text/javascript"
.
그래서 레이블이 붙은 블록 을 사용하여 비슷하지만 약간 다른 솔루션을 고안했습니다 . 이는 래퍼 함수를 만들지 않고 태그 break
에서 구할 수있는 유일한 방법 <script>
입니다 (불필요한).
<!DOCTYPE html>
<script id="worker1">
worker: { // Labeled block wrapper
if (typeof window === 'object') break worker; // Bail if we're not a Worker
self.onmessage = function(e) {
self.postMessage('msg from worker');
};
// Rest of your worker code goes here.
}
</script>
<script>
var blob = new Blob([
document.querySelector('#worker1').textContent
], { type: "text/javascript" })
// Note: window.webkitURL.createObjectURL() in Chrome 10+.
var worker = new Worker(window.URL.createObjectURL(blob));
worker.onmessage = function(e) {
console.log("Received: " + e.data);
}
worker.postMessage("hello"); // Start the worker.
</script>
간단한 promisified 버전, Function#callAsWorker
thisArg 및 인수를 (단지 같은 소요 call
), 그리고 약속을 반환합니다 :
Function.prototype.callAsWorker = function (...args) {
return new Promise( (resolve, reject) => {
const code = `self.onmessage = e => self.postMessage((${this.toString()}).call(...e.data));`,
blob = new Blob([code], { type: "text/javascript" }),
worker = new Worker(window.URL.createObjectURL(blob));
worker.onmessage = e => (resolve(e.data), worker.terminate());
worker.onerror = e => (reject(e.message), worker.terminate());
worker.postMessage(args);
});
}
// Demo
function add(...nums) {
return nums.reduce( (a,b) => a+b );
}
// Let the worker execute the above function, with the specified arguments
add.callAsWorker(null, 1, 2, 3).then(function (result) {
console.log('result: ', result);
});
나는 이와 같은 코드를 사용한다. onmessage를 일반 텍스트 이외의 함수로 정의 할 수 있으므로 편집기가 코드를 강조 표시하고 jshint 작업을 수행 할 수있다.
const worker = createWorker();
createWorker() {
const scriptContent = getWorkerScript();
const blob = new Blob([
scriptContent,
], {
type: "text/javascipt"
});
const worker = new Worker(window.URL.createObjectURL(blob));
return worker;
}
getWorkerScript() {
const script = {
onmessage: function (e) {
console.log(e);
let result = "Hello " + e.data
postMessage(result);
}
};
let content = "";
for (let prop in script){
content += `${prop}=${script[prop].toString()}`;
}
return content;
}
예, 가능합니다. Blob 파일을 사용하여 콜백을 전달했습니다.
필자가 작성한 클래스의 기능과 백그라운드에서 콜백 실행을 관리하는 방법을 보여 드리겠습니다.
먼저 GenericWebWorker
에서 실행할 콜백에 전달할 데이터를 사용 하여를 인스턴스화합니다. Web Worker
여기에는 사용할 함수,이 경우 숫자, 날짜 및 함수가 포함됩니다.blocker
var worker = new GenericWebWorker(100, new Date(), blocker)
이 차단 기능은 n 밀리 초 동안 무한대로 실행됩니다
function blocker (ms) {
var now = new Date().getTime();
while(true) {
if (new Date().getTime() > now +ms)
return;
}
}
그리고 당신은 이것을 이렇게 사용합니다
worker.exec((num, date, fnBlocker) => {
/*Everithing here does not block the main thread
and this callback has access to the number, date and the blocker */
fnBlocker(10000) //All of this run in backgrownd
return num*10
}).then(d => console.log(d)) //Print 1000
이제 아래 예에서 마술을 볼 시간입니다.
/*https://github.com/fercarvo/GenericWebWorker*/
class GenericWebWorker {
constructor(...ags) {
this.args = ags.map(a => (typeof a == 'function') ? {type:'fn', fn:a.toString()} : a)
}
async exec(cb) {
var wk_string = this.worker.toString();
wk_string = wk_string.substring(wk_string.indexOf('{') + 1, wk_string.lastIndexOf('}'));
var wk_link = window.URL.createObjectURL( new Blob([ wk_string ]) );
var wk = new Worker(wk_link);
wk.postMessage({ callback: cb.toString(), args: this.args });
var resultado = await new Promise((next, error) => {
wk.onmessage = e => (e.data && e.data.error) ? error(e.data.error) : next(e.data);
wk.onerror = e => error(e.message);
})
wk.terminate(); window.URL.revokeObjectURL(wk_link);
return resultado
}
async parallel(arr, cb) {
var res = [...arr].map(it => new GenericWebWorker(it, ...this.args).exec(cb))
var all = await Promise.all(res)
return all
}
worker() {
onmessage = async function (e) {
try {
var cb = new Function(`return ${e.data.callback}`)();
var args = e.data.args.map(p => (p.type == 'fn') ? new Function(`return ${p.fn}`)() : p);
try {
var result = await cb.apply(this, args); //If it is a promise or async function
return postMessage(result)
} catch (e) { throw new Error(`CallbackError: ${e}`) }
} catch (e) { postMessage({error: e.message}) }
}
}
}
function blocker (ms) {
var now = new Date().getTime();
while(true) {
if (new Date().getTime() > now +ms)
return;
}
}
setInterval(()=> console.log("Not blocked " + Math.random()), 1000)
console.log("\n\nstarting blocking code in Worker\n\n")
var worker = new GenericWebWorker(100, new Date(), blocker)
worker.exec((num, date, fnBlocker) => {
fnBlocker(7000) //All of this run in backgrownd
return num*10
})
.then(d => console.log(`\n\nEnd of blocking code: result ${d}\n\n`)) //Print 1000
worker.js 파일의 내용을 backticks 안에 넣고 (여러 줄 문자열 상수 허용) 다음과 같이 blob에서 worker를 만들 수 있습니다.
var workerScript = `
self.onmessage = function(e) {
self.postMessage('message from worker');
};
// rest of worker code goes here
`;
var worker =
new Worker(createObjectURL(new Blob([workerScript], { type: "text/javascript" })));
어떤 이유로 든 작업자에 대해 별도의 스크립트 태그를 원하지 않는 경우에 유용합니다.
또 다른 해결책은 Worker를 함수로 감싸고 함수를 호출하는 Blob을 만드는 것입니다.
function workerCode() {
self.onmessage = function (e) {
console.log("Got message from parent", e.data);
};
setTimeout(() => {
self.postMessage("Message From Worker");
}, 2000);
}
let blob = new Blob([
"(" + workerCode.toString() + ")()"
], {type: "text/javascript"});
// Note: window.webkitURL.createObjectURL() in Chrome 10+.
let worker = new Worker(window.URL.createObjectURL(blob));
worker.onmessage = function (e) {
console.log("Received: " + e.data);
};
worker.postMessage("hello"); // Start the worker.
참고 URL : https://stackoverflow.com/questions/5408406/web-workers-without-a-separate-javascript-file
'Programing' 카테고리의 다른 글
신뢰 저장소 및 키 저장소 정의 (0) | 2020.03.24 |
---|---|
aria-label은 무엇이며 어떻게 사용해야합니까? (0) | 2020.03.24 |
Thrift와 프로토콜 버퍼의 가장 큰 차이점은 무엇입니까? (0) | 2020.03.24 |
악용 가능한 PHP 함수 (0) | 2020.03.24 |
Java에서 Sprintf에 해당 (0) | 2020.03.24 |