คนทำงานเว็บที่ไม่มีไฟล์ Javascript แยกต่างหาก?


291

เท่าที่ฉันสามารถบอกได้ผู้ทำงานเว็บต้องเขียนไฟล์ JavaScript แยกต่างหากและเรียกสิ่งนี้:

new Worker('longrunning.js')

ฉันใช้คอมไพเลอร์แบบปิดเพื่อรวมและย่อขนาดซอร์สโค้ด JavaScript ทั้งหมดของฉันและฉันไม่ต้องการให้คนงานของฉันแยกไฟล์เพื่อแจกจ่าย มีวิธีการทำเช่นนี้?

new Worker(function() {
    //Long-running work here
});

เนื่องจากฟังก์ชั่นชั้นหนึ่งมีความสำคัญอย่างยิ่งต่อ JavaScript ทำไมวิธีมาตรฐานในการทำงานพื้นหลังจึงต้องโหลดไฟล์ JavaScript อื่นทั้งหมดจากเว็บเซิร์ฟเวอร์


7
มันเป็นเพราะการรักษาบริบทประหารอย่างหมดจดด้ายยิ่งมีความสำคัญมากกว่าฟังก์ชั่นชั้นแรก :-)
Pointy

1
ผมทำงานกับมัน (หรือมากกว่าในการลดปัญหา): DynWorker คุณสามารถทำได้: var worker = new DynWorker(); worker.inject("foo", function(){...});...
Félix Saparelli

2
ที่เกี่ยวข้อง: วิธีการสร้าง Web Worker จากสตริงและการเรียนการสอนของผู้ปฏิบัติงานที่จะยอมรับรหัสแทนแฟ้มแหล่งที่มาจาวาสคริปต์
Rob W

1
OP ลบคำถาม "Teaching Worker เพื่อยอมรับฟังก์ชั่นแทนไฟล์ต้นฉบับ JavaScript" คำตอบจะโพสต์ที่นี่
Rob W

ฉันพัฒนาtask.jsเพื่อทำให้ง่ายขึ้นมาก ส่วนใหญ่คุณจะพยายามลดภาระการล็อกขนาดเล็กเท่านั้น
Chad Scira

คำตอบ:


225

http://www.html5rocks.com/en/tutorials/workers/basics/#toc-inlineworkers

จะเป็นอย่างไรถ้าคุณต้องการสร้างสคริปต์ผู้ปฏิบัติงานของคุณในทันทีหรือสร้างเพจที่มีในตัวเองโดยไม่ต้องสร้างไฟล์ผู้ปฏิบัติงานแยกต่างหาก ด้วย Blob () คุณสามารถ "inline" คนงานของคุณในไฟล์ HTML เดียวกับตรรกะหลักของคุณโดยการสร้างการจัดการ URL ให้กับรหัสผู้ปฏิบัติงานเป็นสตริง


ตัวอย่างเต็มรูปแบบของผู้ปฏิบัติงานในบรรทัด 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>


โซลูชันของ Google Chrome เท่านั้นดูเหมือนว่า Firefox 10 จะรองรับฉันไม่รู้เกี่ยวกับเบราว์เซอร์อื่น
4esn0k

2
BlobBuiler จะเลิกตอนนี้ ใช้Blobแทน ขณะนี้รองรับ Firefox / WebKit / Opera และ IE10 ล่าสุดดูตารางความเข้ากันได้ของเบราว์เซอร์รุ่นเก่า
Félix Saparelli

3
คอนสตรัคหยดอาจจะมีการสนับสนุนใน IE10 แต่คุณยังคงไม่สามารถผ่าน JavaScript เพื่อให้ผู้ปฏิบัติงานเว็บผ่านมัน (ไม่ได้แม้ใน IE11): connect.microsoft.com/IE/feedback/details/801810/...
jayarjo

1
@albanx - การทดสอบคืออะไร? มีหน้าตัวอย่างเป็นพันล้านทางออนไลน์อยู่แล้วซึ่งแสดงให้เห็นว่าการทำเกลียวไม่วางสายเบราว์เซอร์นานหลายปี
vsync

2
@albanx - อย่างน้อยคุณจะต้องบอกว่าเบราว์เซอร์ที่ลึกลับที่คุณใช้ซึ่งแขวน การสาธิตนี้ไม่แฮงค์ให้คุณ? ie.microsoft.com/testdrive/Graphics/WorkerFountains/…
vsync

162

โซลูชัน html5rocks ของการฝังโค้ดผู้ทำงานเว็บใน HTML นั้นค่อนข้างน่ากลัว
และหยดของ JavaScript-as-a-string ที่หนีออกมาก็ไม่ได้ดีไปกว่านั้นเพราะมันทำให้ขั้นตอนการทำงานยุ่งยาก

โดยส่วนตัวแล้วฉันชอบวิธี toString แต่@ dan-manนั่น regex!

แนวทางที่ฉันชอบ:

// 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 );

การสนับสนุนคือจุดตัดของตารางทั้งสามนี้:

สิ่งนี้จะไม่ทำงานสำหรับSharedWorkerอย่างไรก็ตามเนื่องจาก URL จะต้องตรงกันทุกประการแม้ว่าพารามิเตอร์ 'name' ที่เป็นทางเลือกจะตรงกัน สำหรับ SharedWorker คุณจะต้องใช้ไฟล์ JavaScript แยกต่างหาก


การอัปเดตในปี 2558 - ผู้ใช้ ServiceWorker มาถึงแล้ว

ขณะนี้มีวิธีที่มีประสิทธิภาพยิ่งขึ้นในการแก้ปัญหานี้ อีกครั้งให้เก็บรหัสผู้ปฏิบัติงานเป็นฟังก์ชัน (แทนที่จะเป็นสตริงคงที่) และแปลงโดยใช้. toString () จากนั้นแทรกรหัสลงใน CacheStorage ภายใต้ URL คงที่ที่คุณเลือก

// 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 ดังกล่าวข้างต้นหรือมากกว่าให้ใส่ไฟล์ JavaScript จริงที่ /my_workers/worker1.js

ข้อดีของวิธีนี้คือ:

  1. SharedWorkers สามารถรองรับได้
  2. แท็บสามารถแชร์สำเนาแคชเดียวที่ที่อยู่คงที่ วิธีการ blob proliferates objectURLs สุ่มสำหรับทุกแท็บ

4
ความเข้ากันได้ของเบราว์เซอร์มีลักษณะอย่างไรในโซลูชันนี้
เบ็นดิลต์

คุณอธิบายรายละเอียดเกี่ยวกับโซลูชันนี้ได้อย่างไรมันทำงานอย่างไร คนทำงานคืออะไร มันเป็นไฟล์ js แยกหรือไม่? ฉันพยายามใช้สิ่งนี้ แต่ไม่สามารถใช้งานได้ โดยเฉพาะฉันพยายามทำให้มันใช้งานได้สำหรับ SharedWorker
Yehuda

ถ้าเพียงคุณเท่านั้นที่สามารถสรุปมันได้ในฟังก์ชั่นที่มีประโยชน์!
mmm

@ Ben Dilts: ความเข้ากันได้ของเบราว์เซอร์จะดูเหมือนเพียงแค่เรียกใช้รหัสของคุณผ่าน babel: babeljs.io/repl
Jack Giffin

มาตรฐานไม่รับประกันว่า Function.prototype.toString () จะคืนค่าฟังก์ชันของฟังก์ชันเป็นสตริง คุณควรเพิ่มคำเตือนลงในคำตอบ
RD

37

คุณสามารถสร้างไฟล์ 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นั้นสัมพันธ์กับหน้าหลักไม่ใช่สคริปต์


4
ดูเหมือนว่าเว็บไซต์ของคุณจะหายไป คุณมี URL ใหม่ไหม
BrianFreud

1
นี่เป็นวิธีการที่น่าสนใจ FWIW ฉันตรวจสอบคุณสมบัติ Web Workers โดยการตรวจสอบว่ามี "self" (วัตถุโกลบอล Web Worker) เทียบกับ "หน้าต่าง"
pwnall

ฉันได้ดูว่า PapaParse จัดการกับ Web Workers ได้อย่างไรและพวกเขาดูเหมือนจะใช้วิธีนี้github.com/mholt/PapaParse
JP DeVries

ฉันคิดว่าการทดสอบโดยใช้ 'typeof importScripts! == null' สามารถบอกได้ว่าสคริปต์ทำงานในขอบเขตของผู้ปฏิบัติงานหรือไม่
MeTTeO

1
ฉันไม่เข้าใจสิ่งที่พี่น้องก่อนหน้านี้มาจากองค์ประกอบสคริปต์ มีคนอธิบายฉันได้ไหม
Teemoh

28

ใช้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:

ลิงค์ด้านบนคือส่วนสำคัญที่ฉันสร้างขึ้น คนอื่นต่อมากลายเป็นrepo ที่เกิดขึ้นจริง


11

คนทำงานเว็บดำเนินงานในบริบทที่แยกกันอย่างสิ้นเชิงในฐานะโปรแกรมเฉพาะของแต่ละบุคคล

ซึ่งหมายความว่าไม่สามารถย้ายรหัสจากบริบทหนึ่งไปอีกรูปแบบหนึ่งในรูปแบบวัตถุได้เนื่องจากจะสามารถอ้างอิงวัตถุผ่านการปิดที่เป็นของบริบทอื่นได้
สิ่งนี้มีความสำคัญอย่างยิ่งเนื่องจาก ECMAScript ได้รับการออกแบบให้เป็นภาษาเธรดเดี่ยวและเนื่องจากผู้ปฏิบัติงานเว็บทำงานในเธรดแยกกันดังนั้นคุณจะมีความเสี่ยงต่อการดำเนินการที่ไม่ปลอดภัยต่อเธรด

นี่หมายถึงอีกครั้งว่าผู้ทำงานเว็บจะต้องเริ่มต้นด้วยรหัสในรูปแบบแหล่งที่มา

ข้อมูลจำเพาะจากWHATWGพูดว่า

หากต้นกำเนิดของ URL สัมบูรณ์ที่ได้นั้นไม่เหมือนกับที่มาของสคริปต์การเข้าใช้ให้โยนข้อยกเว้น SECURITY_ERR

ดังนั้นสคริปต์ต้องเป็นไฟล์ภายนอกที่มีรูปแบบเดียวกับหน้าดั้งเดิม: คุณไม่สามารถโหลดสคริปต์จากข้อมูล: URL หรือ javascript: URL และหน้า https: ไม่สามารถเริ่มทำงานโดยใช้สคริปต์ที่มี http: URL

แต่น่าเสียดายที่มันไม่ได้อธิบายอย่างแท้จริงว่าทำไมไม่มีใครอนุญาตให้ส่งผ่านสตริงที่มีซอร์สโค้ดไปยังตัวสร้าง


6

วิธีที่ดีกว่าในการอ่านสำหรับคนทำงานแบบอินไลน์

    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"); 

สิ่งที่ฉันทำคือฉันสร้างฟังก์ชั่นโดยใช้รหัสผู้ปฏิบัติงานส่งผ่านฟังก์ชั่นtoString()นั้นเพิ่มปริมาณร่างกายแล้วใส่ลงใน Blob ตรวจสอบคำตอบสุดท้ายฉันมีตัวอย่าง
Fernando Carvajal

5

การตอบสนองของ Adria และวางไว้ในฟังก์ชั่นคัดลอกวางซึ่งทำงานได้กับ Chrome และ FF ปัจจุบัน แต่ไม่ใช่ IE10 (คนงานจากหยดทำให้เกิดข้อผิดพลาดด้านความปลอดภัย )

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/


5

คำตอบล่าสุด (2018)

คุณสามารถใช้Greenlet :

ย้ายฟังก์ชั่น async ไปยังเธรดของตัวเอง 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'))

3

ขึ้นอยู่กับกรณีการใช้งานของคุณคุณสามารถใช้สิ่งที่ชอบ

task.jsอินเทอร์เฟซแบบง่ายสำหรับการรับ CPU Intensive Code เพื่อรันบนคอร์ทั้งหมด (node.js และเว็บ)

ตัวอย่างจะเป็น

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
});

2

ลองดูที่ปลั๊กอิน vkThread ด้วยปลั๊กอิน htis คุณสามารถใช้ฟังก์ชั่นใด ๆ ในโค้ดหลักของคุณและรันมันในเธรด (ผู้ปฏิบัติงานเว็บ) ดังนั้นคุณไม่จำเป็นต้องสร้าง "ไฟล์ผู้ทำงานเว็บ" เป็นพิเศษ

http://www.eslinstructor.net/vkthread/

--Vadim


1

คุณสามารถใช้เว็บเวิร์คในจาวาสคริปต์เดียวกันโดยใช้เว็บเวิร์คไลน์

บทความด้านล่างนี้จะช่วยให้คุณเข้าใจเว็บมาสเตอร์และข้อ จำกัด และการดีบักของเว็บเวิร์คได้อย่างง่ายดาย

การเรียนรู้ในเว็บเวิร์ค


1

ฉันคิดว่าวิธีที่ดีกว่าในการทำเช่นนี้คือการใช้วัตถุ 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"); 


1

ที่นี่คอนโซล:

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);
});

1

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
    });
  };

})();


1

ดังนั้นฉันคิดว่าเรามีตัวเลือกที่ยอดเยี่ยมสำหรับตอนนี้ขอบคุณเทมเพลตตัวอักษรใน 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 ที่แต่ละอันเพื่อลดพวกมันลงในสตริงเช่นกัน (ควรทำงานตราบเท่าที่พวกเขาประกาศฟังก์ชัน) จากนั้นเพียงแค่เตรียมสิ่งนั้นลงในสตริงสคริปต์ ด้วยวิธีนี้เราไม่จำเป็นต้อง importScripts ที่เราอาจรวมอยู่ในขอบเขตของโค้ดที่เรากำลังเขียน

ข้อเสียที่แท้จริงเพียงข้อเดียวของรุ่นนี้คือ linters จะไม่สามารถทับรหัสพนักงานบริการ (เนื่องจากเป็นเพียงสายอักขระ) ซึ่งเป็นข้อดีสำหรับ "วิธีแยกฟังก์ชันผู้ปฏิบัติงาน"


1

นี่เป็นเพียงส่วนเพิ่มเติม - ฉันมีเทมเพลตที่ดีสำหรับการทดสอบเว็บเวิร์กใน jsFiddle แทนที่จะใช้ Blob จะใช้ jsFiddles ?jsapi:

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);
  }
})

เว็บเทมเพลตปกติและเทมเพลตผู้ปฏิบัติงานที่แชร์มีให้บริการ


1

ฉันค้นพบว่าในปัจจุบัน 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>


1

เวอร์ชันที่ได้รับการรับรองอย่างง่ายFunction#callAsWorkerซึ่งใช้อาร์กิวเมนต์นี้และอาร์กิวเมนต์ (เช่นเดียวกับ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);
});


คุณควรเพิ่มclose()วิธีการปิดเบ็ดชีวิตของผู้ปฏิบัติงานเว็บของคุณ developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope/ …
Shahar ドーン Levi

@Shahar ドーン Levi closeฟังก์ชั่นเลิกใช้แล้ว อย่างไรก็ตามคนงานสามารถยกเลิกได้ ฉันได้เพิ่มที่ตอนนี้
trincot

0

ฉันใช้รหัสเช่นนี้คุณสามารถกำหนด 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;
}


ดูคำตอบของฉันฉันเพิ่งทำอย่างนั้น แต่ฉันเขียนทั้งชั้นเพื่อสรุปวิธีการโทรกลับ
Fernando Carvajal

0

ใช่เป็นไปได้ฉันใช้ไฟล์ 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


0

คุณสามารถวางเนื้อหาของไฟล์ worker.js ของคุณใน backticks (ซึ่งอนุญาตให้มีค่าคงที่สตริงหลายบรรทัด) และสร้างผู้ทำงานจาก blob ดังนี้:

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" })));

สิ่งนี้มีประโยชน์ถ้าด้วยเหตุผลใดก็ตามที่คุณไม่ต้องการให้มีแท็กสคริปต์แยกต่างหากสำหรับผู้ปฏิบัติงาน


0

วิธีแก้ปัญหาอื่นก็เพื่อห่อตัว 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.

-1

หนึ่งซับสำหรับการใช้งานฟังก์ชั่นในคนงาน:

const FunctionalWorker = fn => new Worker(window.URL.createObjectURL(new Blob(["(" + workerCode.toString() + ")()"], {type: "text/javascript"})));

ตัวอย่างการใช้งาน:

let fn = FunctionalWorker(() => {
    self.postMessage("hi");
});
fn.onmessage = msg => {
    console.log(msg);
};
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.