ตอนนี้การย้ายการยกของหนักไปยังเทรดเบื้องหลังทำได้ง่ายขึ้นด้วยโมดูล JavaScript ใน Web Worker
JavaScript ทำงานแบบเธรดเดียว ซึ่งหมายความว่าจะทำงานได้ครั้งละ 1 รายการเท่านั้น นี่คือ ใช้งานง่ายและทำงานได้ดีในหลายๆ กรณีบนเว็บ แต่อาจกลายเป็นปัญหาเมื่อเราจำเป็นต้อง ทำงานที่ต้องใช้แรงมากมาย เช่น การประมวลผลข้อมูล การแยกวิเคราะห์ การคำนวณ หรือการวิเคราะห์ เพิ่มมากขึ้นเรื่อยๆ แอปพลิเคชันที่ซับซ้อนจะถูกส่งบนเว็บ ทำให้มีความต้องการสำหรับหลายเธรดเพิ่มขึ้น การประมวลผล
บนแพลตฟอร์มเว็บ ค่าพื้นฐานที่สำคัญสำหรับการจัดชุดข้อความและการทำงานพร้อมกันคือ เว็บ Workers API ผู้ปฏิบัติงานเป็นนามธรรมขนาดเล็กที่อยู่เหนือระบบปฏิบัติการ ชุดข้อความที่เปิดเผย API ที่ส่งผ่านข้อความ สำหรับการสื่อสารระหว่างชุดข้อความ ซึ่งอาจเป็นประโยชน์อย่างมากเมื่อมีการคำนวณค่าใช้จ่ายสูง หรือ ดำเนินการบนชุดข้อมูลขนาดใหญ่ ทำให้เทรดหลักทำงานได้อย่างราบรื่นขณะดำเนินการ การดำเนินการที่มีค่าใช้จ่ายสูงในชุดข้อความเบื้องหลังอย่างน้อย 1 รายการ
ต่อไปนี้เป็นตัวอย่างการใช้งานโดยทั่วไปของผู้ปฏิบัติงาน ซึ่งสคริปต์ผู้ปฏิบัติงานจะคอยฟังข้อความจากข้อความหลัก ชุดข้อความและตอบกลับโดยส่งข้อความกลับมาของตนเอง:
page.js:
const worker = new Worker('worker.js');
worker.addEventListener('message', e => {
console.log(e.data);
});
worker.postMessage('hello');
worker.js:
addEventListener('message', e => {
if (e.data === 'hello') {
postMessage('world');
}
});
Web Worker API มีให้ใช้งานในเบราว์เซอร์ส่วนใหญ่มานานกว่า 10 ปีแล้ว ขณะที่ หมายความว่าพนักงานมีการสนับสนุนเบราว์เซอร์ที่ดีเยี่ยมและได้รับการเพิ่มประสิทธิภาพอย่างเหมาะสมแล้ว โมดูล JavaScript ที่เก่ากว่านี้ เนื่องจากเมื่อผู้ปฏิบัติงานออกแบบระบบโมดูลใหม่ไม่ได้ API สำหรับการโหลดโค้ดลงในผู้ปฏิบัติงาน และการเขียนสคริปต์ยังคงมีลักษณะคล้ายกับสคริปต์แบบซิงโครนัส วิธีโหลดที่ใช้กันทั่วไปในปี 2009
ประวัติ: ผู้ปฏิบัติงานแบบคลาสสิก
ตัวสร้างของผู้ปฏิบัติงานใช้
Script URL ซึ่งก็คือ
ที่สัมพันธ์กับ URL ของเอกสาร โดยจะส่งกลับการอ้างอิงไปยังอินสแตนซ์ของผู้ปฏิบัติงานใหม่ทันที
ซึ่งแสดงอินเทอร์เฟซการรับส่งข้อความ รวมถึงเมธอด terminate()
ที่จะหยุดและ
ทำลายผู้ปฏิบัติงาน
const worker = new Worker('worker.js');
ฟังก์ชัน importScripts()
มีให้ใช้งานภายใน Web Worker สำหรับโหลดโค้ดเพิ่มเติม แต่
หยุดการดำเนินการของผู้ปฏิบัติงานชั่วคราวเพื่อดึงข้อมูลและประเมินแต่ละสคริปต์ และยังเรียกใช้สคริปต์ด้วย
ในขอบเขตรวมทั้งหมด เช่น แท็ก <script>
เดิม ซึ่งหมายความว่าตัวแปรต่างๆ ในสคริปต์หนึ่งอาจเป็น
ถูกเขียนทับโดยตัวแปรในอีก
worker.js:
importScripts('greet.js');
// ^ could block for seconds
addEventListener('message', e => {
postMessage(sayHello());
});
greet.js:
// global to the whole worker
function sayHello() {
return 'world';
}
ด้วยเหตุนี้ ที่ผ่านมาผู้ปฏิบัติงานบนเว็บจึงสร้างผลกระทบอย่างมากต่อสถาปัตยกรรมของ
แอปพลิเคชัน นักพัฒนาซอฟต์แวร์ต้องสร้างเครื่องมือที่ชาญฉลาดและวิธีแก้ปัญหาเบื้องต้นเพื่อให้สามารถ
ใช้ผู้ปฏิบัติงานบนเว็บได้โดยไม่ต้องเลิกใช้แนวทางการพัฒนาสมัยใหม่ ตัวอย่างเช่น Bundler เช่น
Webpack จะฝังการใช้งานตัวโหลดโมดูลขนาดเล็กลงในโค้ดที่สร้างขึ้นซึ่งใช้ importScripts()
สำหรับการโหลดโค้ด แต่จะรวมโมดูลในฟังก์ชันเพื่อหลีกเลี่ยงการขัดแย้งของตัวแปรและจำลอง
การนำเข้าและส่งออกทรัพยากร Dependency
ป้อนผู้ปฏิบัติงานโมดูล
โหมดใหม่สำหรับผู้ปฏิบัติงานบนเว็บที่มาพร้อมหลักการยศาสตร์และประโยชน์ด้านประสิทธิภาพของ JavaScript
โมดูลจัดส่งใน Chrome 80 ซึ่งเรียกว่า "ผู้ปฏิบัติงานโมดูล"
ตัวสร้าง Worker
ยอมรับตัวเลือก {type:"module"}
ใหม่ ซึ่งจะเปลี่ยนการโหลดสคริปต์และ
ให้ตรงกับ <script type="module">
const worker = new Worker('worker.js', {
type: 'module'
});
เนื่องจากผู้ปฏิบัติงานโมดูลเป็นโมดูล JavaScript มาตรฐาน จึงสามารถใช้คำสั่งนำเข้าและส่งออก อาส พร้อมกับโมดูล JavaScript ทั้งหมด ทรัพยากร Dependency จะดำเนินการเพียงครั้งเดียวในบริบทที่กำหนด (เทรดหลัก ผู้ปฏิบัติงาน เป็นต้น) และการนำเข้าทั้งหมดในอนาคตจะอ้างอิงอินสแตนซ์ของโมดูลที่ได้ดำเนินการไปแล้ว การโหลด และการเรียกใช้โมดูล JavaScript ก็ได้รับการเพิ่มประสิทธิภาพโดยเบราว์เซอร์ ทรัพยากร Dependency ของโมดูล โหลดก่อนที่จะเรียกใช้โมดูล ซึ่งทำให้สามารถโหลดต้นไม้โมดูลทั้งโมดูลใน พร้อมกัน การโหลดโมดูลยังแคชโค้ดที่แยกวิเคราะห์ไว้ด้วย ซึ่งหมายความว่าโมดูลที่ใช้ในหน้าหลัก เทรดและในผู้ปฏิบัติงานต้องได้รับการแยกวิเคราะห์เพียงครั้งเดียว
การย้ายไปยังโมดูล JavaScript ยังทำให้สามารถใช้ dynamic
นำเข้าสำหรับโค้ดการโหลดแบบ Lazy Loading โดยไม่บล็อกการดำเนินการ
ผู้ปฏิบัติงาน การนำเข้าแบบไดนามิกมีความชัดเจนมากกว่าการใช้ importScripts()
เพื่อโหลดทรัพยากร Dependency
เนื่องจากระบบจะส่งคืนการส่งออกของโมดูลที่นำเข้าแทนที่จะพึ่งพาตัวแปรร่วม
worker.js:
import { sayHello } from './greet.js';
addEventListener('message', e => {
postMessage(sayHello());
});
greet.js:
import greetings from './data.js';
export function sayHello() {
return greetings.hello;
}
วิธี importScripts()
แบบเก่าจะไม่พร้อมใช้งานภายในโมดูลเพื่อให้มั่นใจได้ถึงประสิทธิภาพที่ยอดเยี่ยม
ผู้ปฏิบัติงาน การเปลี่ยนให้ผู้ปฏิบัติงานใช้โมดูล JavaScript หมายความว่าโค้ดทั้งหมดจะโหลดเป็นแบบเข้มงวด
โหมด เพิ่มอีก
การเปลี่ยนแปลงที่เห็นได้ชัดคือค่าของ this
ในขอบเขตระดับบนสุดของโมดูล JavaScript คือ
undefined
ในขณะที่สำหรับผู้ปฏิบัติงานแบบเดิม ค่านี้คือขอบเขตโดยรวมของผู้ปฏิบัติงาน โชคดีที่
เป็นself
สากลที่อ้างอิงถึงขอบเขตระดับโลกมาโดยตลอด มีให้บริการใน
คนทำงานทุกประเภท รวมถึง Service Worker และ DOM
โหลดผู้ปฏิบัติงานล่วงหน้าด้วย modulepreload
การปรับปรุงประสิทธิภาพที่สำคัญอย่างหนึ่งที่มาพร้อมกับผู้ปฏิบัติงานโมดูลก็คือความสามารถในการโหลดล่วงหน้า
และทรัพยากร Dependency ต่างๆ เมื่อใช้ผู้ปฏิบัติงานโมดูล สคริปต์จะโหลดและดำเนินการตามมาตรฐาน
โมดูล JavaScript ซึ่งหมายความว่าสามารถโหลดล่วงหน้าและแยกวิเคราะห์ล่วงหน้าได้โดยใช้ modulepreload
:
<!-- preloads worker.js and its dependencies: -->
<link rel="modulepreload" href="worker.js">
<script>
addEventListener('load', () => {
// our worker code is likely already parsed and ready to execute!
const worker = new Worker('worker.js', { type: 'module' });
});
</script>
นอกจากนี้ ทั้งผู้ปฏิบัติงานเทรดหลักและโมดูลยังใช้โมดูลที่โหลดไว้ล่วงหน้าได้ด้วย วิธีนี้เป็นประโยชน์สำหรับ โมดูลที่นำเข้าทั้ง 2 บริบท หรือในกรณีที่ไม่ทราบล่วงหน้า จะใช้โมดูลในเทรดหลักหรือในผู้ปฏิบัติงาน
ก่อนหน้านี้ ตัวเลือกที่มีให้สำหรับการโหลดสคริปต์ของ Web Worker ล่วงหน้านั้นมีจำกัดและ
เชื่อถือได้ ผู้ปฏิบัติงานแบบดั้งเดิมมี "คนทำงาน" ของตนเอง ประเภททรัพยากรสำหรับการโหลดล่วงหน้า แต่ไม่มี
เบราว์เซอร์ที่ใช้ <link rel="preload" as="worker">
ด้วยเหตุนี้ เทคนิคหลัก
สำหรับการโหลดล่วงหน้าของผู้ปฏิบัติงานบนเว็บคือการใช้ <link rel="prefetch">
ซึ่งอาศัยทั้งหมด
ในแคช HTTP การใช้ควบคู่กับ Headers การสร้างแคชที่ถูกต้องจะช่วยให้
เพื่อหลีกเลี่ยงการสร้างอินสแตนซ์ให้กับผู้ปฏิบัติงานที่ต้องรอดาวน์โหลดสคริปต์ผู้ปฏิบัติงาน แต่สิ่งที่ต่างจาก
modulepreload
เทคนิคนี้ไม่รองรับการโหลดทรัพยากร Dependency ล่วงหน้าหรือการแยกวิเคราะห์ล่วงหน้า
แล้วผู้ปฏิบัติงานที่แชร์เป็นอย่างไร
ผู้ปฏิบัติงานที่แชร์ได้ดำเนินการต่อไปนี้
ได้รับการอัปเดตให้รองรับโมดูล JavaScript ใน Chrome 83 แล้ว เช่นเดียวกับผู้ปฏิบัติงานที่ทุ่มเท
การสร้างคนทำงานที่แชร์ด้วยตัวเลือก {type:"module"}
ตอนนี้จะโหลดสคริปต์ผู้ปฏิบัติงานเป็น
แทนที่จะเป็นสคริปต์แบบคลาสสิก
const worker = new SharedWorker('/worker.js', {
type: 'module'
});
ก่อนที่จะสนับสนุนโมดูล JavaScript ตัวสร้าง SharedWorker()
ต้องการเพียง
URL และอาร์กิวเมนต์ name
ที่ไม่บังคับ โดยจะยังคงใช้งานได้ต่อไปเมื่อใช้ผู้ปฏิบัติงานที่แชร์แบบคลาสสิก อย่างไรก็ตาม
การสร้างผู้ปฏิบัติงานที่แชร์โมดูลจำเป็นต้องใช้อาร์กิวเมนต์ options
ใหม่ พร้อมใช้งาน
ตัวเลือก
เหมือนกันกับตัวเลือกของผู้ปฏิบัติงานเฉพาะ ซึ่งรวมถึงตัวเลือก name
ที่จะมีผลแทน
อาร์กิวเมนต์ name
ก่อนหน้า
แล้ว Service Worker ล่ะ
ข้อกำหนดของโปรแกรมทำงานของบริการมีอยู่แล้ว
อัปเดตแล้วเพื่อรองรับการยอมรับ
โมดูล JavaScript เป็นจุดเริ่มต้น โดยใช้ตัวเลือก {type:"module"}
เดียวกับผู้ปฏิบัติงานโมดูล
แต่การเปลี่ยนแปลงนี้ยังไม่ได้ใช้กับเบราว์เซอร์ เมื่อเกิดเหตุการณ์ดังกล่าวขึ้น คุณก็มีโอกาส
เพื่อสร้างอินสแตนซ์ Service Worker โดยใช้โมดูล JavaScript โดยใช้โค้ดต่อไปนี้
navigator.serviceWorker.register('/sw.js', {
type: 'module'
});
เนื่องจากตอนนี้มีการอัปเดตข้อกำหนดแล้ว เบราว์เซอร์จะเริ่มนำลักษณะการทำงานแบบใหม่มาใช้ การดำเนินการนี้ต้องใช้เวลาเนื่องจากมีข้อมูลแทรกเพิ่มเติมที่เชื่อมโยงกับการนำ JavaScript Service Worker การลงทะเบียนโปรแกรมทำงานของบริการต้องเปรียบเทียบสคริปต์ที่นำเข้า กับเวอร์ชันที่แคชไว้ก่อนหน้านี้เมื่อ กำหนดว่าจะทริกเกอร์การอัปเดตหรือไม่ และต้องมีการติดตั้งใช้งานสำหรับโมดูล JavaScript เมื่อใช้สำหรับ Service Worker นอกจากนี้ โปรแกรมทำงานของบริการต้องข้าม แคช สำหรับสคริปต์ในบางกรณีเมื่อ กำลังตรวจหาการอัปเดต
แหล่งข้อมูลเพิ่มเติมและอ่านเพิ่มเติม
- สถานะฟีเจอร์ ความเห็นพ้องของเบราว์เซอร์ และการกำหนดมาตรฐาน
- การเพิ่มข้อกำหนดของผู้ปฏิบัติงานโมดูลเดิม
- โมดูล JavaScript สำหรับผู้ปฏิบัติงานที่แชร์
- โมดูล JavaScript สำหรับ Service Worker: สถานะการใช้งาน Chrome