จาวาสคริปต์เธรดเดี่ยว
เธรดเดี่ยวของ JavaScript เกี่ยวข้องกับวัตถุประสงค์ ในฐานะที่เป็นภาษาสคริปต์เบราว์เซอร์จุดประสงค์หลักของ JavaScript คือการโต้ตอบกับผู้ใช้และใช้งาน DOM สิ่งนี้กำหนดว่ามันสามารถเป็นเกลียวเดี่ยวได้มิฉะนั้นจะทำให้เกิดปัญหาการซิงโครไนซ์ที่ซับซ้อนมาก ตัวอย่างเช่นสมมติว่า JavaScript มีสองเธรดในเวลาเดียวกันเธรดหนึ่งจะเพิ่มเนื้อหาในโหนด DOM บางตัวและเธรดอื่น ๆ จะลบโหนดนี้ซึ่งเบราว์เซอร์ควรใช้เธรดใดในเวลานี้ ดังนั้นเพื่อหลีกเลี่ยงความซับซ้อน JavaScript เป็นเธรดเดียวตั้งแต่แรกเกิดซึ่งได้กลายเป็นคุณลักษณะหลักของภาษานี้และจะไม่เปลี่ยนแปลงในอนาคต
งานคิว
การทำเกลียวเดี่ยวหมายความว่างานทั้งหมดจะต้องเข้าคิวและงานก่อนหน้าจะถูกดำเนินการก่อนที่งานถัดไปจะถูกดำเนินการ หากงานก่อนหน้านี้ใช้เวลานานงานถัดไปจะต้องรอ
ไดรเวอร์เหตุการณ์แบบอะซิงโครนัส
พฤติกรรมจำนวนมากในเบราว์เซอร์เป็นแบบอะซิงโครไนซ์เช่น: เหตุการณ์คลิกเมาส์เหตุการณ์การลากขนาดหน้าต่างเหตุการณ์ทริกเกอร์ตัวจับเวลา XMLHTTTPREQUEST CLACK BRACK ฯลฯ เมื่อเหตุการณ์อะซิงโครนัสเกิดขึ้นมันจะเข้าสู่คิวเหตุการณ์ เบราว์เซอร์มีห่วงข้อความขนาดใหญ่ภายในวง Event Loop ซึ่งจะสำรวจคิวเหตุการณ์ขนาดใหญ่และเหตุการณ์กระบวนการ ตัวอย่างเช่นเบราว์เซอร์กำลังยุ่งอยู่กับการประมวลผลเหตุการณ์ onclick และจากนั้นเหตุการณ์อื่นเกิดขึ้น (เช่นหน้าต่าง onsize) และเหตุการณ์แบบอะซิงโครนัสนี้จะถูกใส่ลงในคิวเหตุการณ์และรอการประมวลผล เหตุการณ์นี้จะถูกดำเนินการเฉพาะเมื่อการประมวลผลก่อนหน้านี้เสร็จสมบูรณ์และฟรี
ลูปเหตุการณ์
JavaScript เป็นเธรดเดี่ยว แต่เบราว์เซอร์ไม่ใช่เธรดเดี่ยว
เบราว์เซอร์จะมีกระบวนการต่อไปนี้อย่างน้อย
1. เบราว์เซอร์ GUI Rendering Thread
2. เธรดเครื่องยนต์ JavaScript
3. เธรดทริกเกอร์ที่กำหนดเวลาเบราว์เซอร์
4. เหตุการณ์เบราว์เซอร์ทริกเกอร์เธรด
5. เบราว์เซอร์ http เธรดคำขอแบบอะซิงโครนัส
เนื่องจากเอ็นจิ้น JavaScript เป็นเธรดเดี่ยวรหัสจึงถูกกดครั้งแรกไปยังคิวและจากนั้นเรียกใช้โดยเครื่องยนต์ในลักษณะแรกในรูปแบบแรก ฟังก์ชั่นการจัดการเหตุการณ์และฟังก์ชั่นการดำเนินการจับเวลาจะถูกวางไว้ในคิวนี้แล้วใช้ลูปที่ไม่มีที่สิ้นสุดเพื่อแยกฟังก์ชั่นออกจากหัวหน้าทีมอย่างต่อเนื่องเพื่อดำเนินการ นี่คือ Event Loop
โดยสรุป JS เป็นเธรดเดี่ยว แต่เบราว์เซอร์เป็นแบบมัลติเธรด เมื่อพบกับสิ่งที่ไม่เปลี่ยนแปลงเบราว์เซอร์จะนำการโทรกลับแบบอะซิงโครนัสลงในลูปเหตุการณ์ เมื่อเธรด JS ไม่ว่างให้อ่าน Event Loop
หลักการจับเวลา
วิธีใช้ตัวจับเวลา
settimeout (fn, ล่าช้า)
setInterval (fn, ล่าช้า)
FN เป็นฟังก์ชั่นหรือสตริงการหน่วงเวลาคือเวลาของการหน่วงเวลาหน่วยเป็นมิลลิวินาที
มีสิ่งต่อไปนี้ควรทราบ
1. แม้ว่า FN สามารถเป็นสตริงได้ แต่ก็ไม่แนะนำให้ใช้เช่นนี้
2. หากมีฟังก์ชั่นนี้ใน FN สิ่งนี้จะชี้ไปที่หน้าต่างเมื่อดำเนินการ
หากคุณเข้าใจหัวข้อเดียวของ JS และ Event Loop อย่างดีหลักการของการจับเวลาจะเข้าใจได้ง่าย
หากตั้งค่าตัวจับเวลาเมื่อถึงเวลาหน่วงเวลาเบราว์เซอร์จะวางเหตุการณ์การดำเนินการล่าช้าลงในลูปเหตุการณ์ เมื่อถึงเวลาหากเธรด JS ไม่ได้ใช้งานมันจะถูกดำเนินการ (ดังนั้นความแม่นยำของตัวจับเวลาจึงไม่ถูกต้อง)
ฉันอ่านบทความเกี่ยวกับความแตกต่างระหว่าง settimeout และ setInterval ที่มักจะโพลฟังก์ชั่น รหัสมีดังนี้
การคัดลอกรหัสมีดังนี้:
settimeout (function () {
settimeout (arguments.callee, 100)
}, 100)
setInterval (function () {}, 1000)
ความหมายทั่วไปของบทความคือ SettimeOut เริ่มจับเวลาถัดไปหลังจากการเรียกใช้ฟังก์ชั่นการเรียกกลับดังนั้นจึงต้องดำเนินการเป็นระยะในขณะที่ SetInterval จะดำเนินการตลอดเวลา หากคุณพบเธรด JS ที่ยังคงดำเนินการอยู่คุณอาจเพิ่มการโทรกลับหลายครั้งใน Event Loop เมื่อเธรด JS ไม่ว่างการประหารชีวิตหลายครั้งจะถูกดำเนินการหลังจากนั้นอีก
หลังจากการทดสอบพบว่า SetInterval อยู่ในช่วงเวลาหนึ่งไม่ว่าจะอยู่ภายใต้ IE, FF, Chrome, Opera และ Safari
รหัสทดสอบมีดังนี้
การคัดลอกรหัสมีดังนี้:
setInterval (function () {
xx.innerhtml = xx.innerhtml+1;
}, 100);
สำหรับ (var i = 0; i <6000000; i ++) {
xx.offsetWidth
-
settimeout (function () {
ดีบักเกอร์;
}, 10)
เมื่อจุดพักยังคงพิมพ์เพียง 1
ปัญหาความแม่นยำของตัวจับเวลา
เนื่องจากเธรดเดี่ยว JS ถ้าคุณพบกับความวุ่นวายตัวจับเวลาจะไม่ถูกต้องแน่นอนและมันจะนานขึ้นเรื่อย ๆ สิ่งนี้ดูเหมือนจะไม่สามารถแก้ไขได้ไม่มีทางออก
ปัญหาความแม่นยำอีกประการหนึ่งคือช่วงเวลาขั้นต่ำ settimout (สนุก, 0)
เมื่อเธรด JS ไม่ว่างมันเป็นไปไม่ได้ที่จะดำเนินการทันทีหลังจาก 0 วินาที มีช่วงเวลาต่ำสุดเสมอและแต่ละเบราว์เซอร์ก็ยังแตกต่างกัน สิ่งนี้ยังไม่ได้รับการทดสอบ
ฉันอ่านบทความเกี่ยวกับมาตรฐานของ W3C การดำเนินการตามเวลาขั้นต่ำของตัวจับเวลาคือ 4ms ไม่มีวิธีตรวจสอบแหล่งที่มาหากคุณไม่พบ! - -
การเพิ่มประสิทธิภาพบางอย่างที่เกี่ยวข้องกับตัวจับเวลา
ยังมีการเพิ่มประสิทธิภาพบางอย่างเมื่อทำการจับเวลา
1. ตัวอย่างเช่นหากคุณผูก window.onresize ทริกเกอร์จะบ่อยมากเมื่อเบราว์เซอร์ถูกซูมดังนั้นคุณสามารถชะลอการดำเนินการ เมื่อการดำเนินการครั้งต่อไปถูกล้างมันจะลดการดำเนินการบ่อยครั้ง
รหัสหลอกมีดังนี้
การคัดลอกรหัสมีดังนี้:
ตัวจับเวลา var;
ฟังก์ชั่น r () {
ClearTimeout (ตัวจับเวลา);
timer = settimeout (function () {
// ทำอะไรบางอย่าง
}, 150);
-
2. เมื่อแถบเลื่อนถูกดึงลงมามันก็เล็กน้อย ตัวอย่างเช่น Lazyload ของภาพควรมีตัวจับเวลาเพื่อหลีกเลี่ยงการคำนวณที่มากเกินไป
3. เมื่อมีหลายสถานที่ที่ต้องการตัวจับเวลาคุณสามารถรวมเข้ากับตัวจับเวลา ช่วงเวลาเป็นช่วงเวลาที่เล็กที่สุด จากนั้นฟังก์ชั่นการโทรกลับที่ต้องดำเนินการจะถูกยัดเข้าไปในอาร์เรย์ เมื่อถึงช่วงเวลาเวลาให้วนซ้ำผ่านอาร์เรย์เพื่อดำเนินการ
การสาธิตเล็ก ๆ
การคัดลอกรหัสมีดังนี้:
<! doctype html>
<html>
<head>
<meta charset = "utf-8" />
<style>
.wrap {ความกว้าง: 80%; มาร์จิ้น: 30px auto; ชายแดน: 1px Solid #CCC; Padding: 20px;}
.C {ชายแดน: 1px Solid #CCC; ความสูง: 30px; ขอบด้านล่าง: 20px;}
</style>
</head>
<body>
<div id = "xx"> </div>
<div>
<div id = "a1"> 0 </div>
<div id = "a2"> 0 </div>
<div id = "a3"> 0 </div>
<div id = "a4"> 0 </div>
</div>
<script src = "http://static.paipaiimg.com/paipai_h5/js/ttj/zepto.min.js"> </script>
<script type = "text/javascript">
var runtime = {
ตัวเลือก: {
ขั้นตอน: 1,000
-
การโทรกลับ: [],
addCallbacks: [],
เริ่มต้น: เท็จ
ตัวจับเวลา: null,
ขยาย: ฟังก์ชั่น () {
var target = อาร์กิวเมนต์ [0] || {}, i = 1, length = arguments.length, ตัวเลือก;
if (typeof target! = "object" && typeof target! = "function")
target = {};
สำหรับ (; i <length; i ++)
if ((ตัวเลือก = อาร์กิวเมนต์ [i])! = null)
สำหรับ (ชื่อ var ในตัวเลือก) {
var copy = ตัวเลือก [ชื่อ];
if (target === สำเนา)
ดำเนินการต่อ;
ถ้า (คัดลอก! == ไม่ได้กำหนด)
เป้าหมาย [ชื่อ] = คัดลอก;
-
เป้าหมายกลับ;
-
init: ฟังก์ชั่น (ตัวเลือก) {
$ .Extend (นี่, this.options, ตัวเลือก || {});
-
เพิ่ม: ฟังก์ชั่น (สนุกตัวเลือก) {
ตัวเลือก = ตัวเลือก || {};
this.addcallbacks.push ({
สนุก: สนุก
เริ่มต้น: วันที่ใหม่ (). getTime (),
ขั้นตอน: ตัวเลือกขั้นตอน || this.step,
ฉัน: 1
-
var self = this;
ถ้า (! this.start) {
this.callbacks = [fun];
this.start = true;
this.starttime = new Date (). getTime ();
this.timer = setInterval (function () {
self.done ();
}, this.step);
-
-
เสร็จแล้ว: function () {
var callbacks = this.callbacks
ตัวเอง = สิ่งนี้
newarr = [];
$ .Each (callbacks, function (i, obj) {
if (obj.step == self.step) {
obj.fun ();
}อื่น{
if (obj.i == obj.step/self.step) {
if ((วันที่ใหม่ (). getTime ())-obj.starttime> obj.step*2/3) {
obj.fun ();
-
obj.i = 1;
}อื่น{
obj.i = obj.i + 1;
-
-
-
$ .Each (this.addcallbacks, function (i, obj) {
if (obj.step == self.step) {
if ((วันที่ใหม่ (). getTime ())-obj.starttime> obj.step*2/3) {
obj.fun ();
การโทรกลับ PUSH (OBJ);
}อื่น{
newarr.push (obj);
-
}อื่น{
obj.i = obj.i + 1;
การโทรกลับ PUSH (OBJ);
-
-
this.addCallbacks = Newarr;
-
ล้าง: ฟังก์ชั่น () {
ClearInterval (this.timer);
-
-
runtime.init ();
runtime.add (function () {
a1.innerhtml = ~~ a1.innerhtml+1;
-
runtime.add (function () {
a2.innerhtml = ~~ a2.innerhtml+1;
}, {ขั้นตอน: 2000});
runtime.add (function () {
a3.innerhtml = ~~ a3.innerhtml+1;
}, {ขั้นตอน: 4000});
runtime.add (function () {
a4.innerhtml = ~~ a4.innerhtml+1;
}, {ขั้นตอน: 8000});
</script>
</body>
</html>
คุณมีความคิดเกี่ยวกับตัวจับเวลา JavaScript หรือไม่? ฝากข้อความถึงฉันหากคุณมีคำถามใด ๆ