11

Tôi có một Web Worker. Tôi muốn thực hiện các yêu cầu mạng định kỳ với nó. Một điều tôi đặc biệt muốn là thực hiện các yêu cầu này ngay cả khi chuỗi thực hiện JS chính bị chặn (ví dụ: bởi window.alert). Tôi đang sử dụng Chrome 38.Công nhân web bị chặn bởi chuỗi chính trong Chrome

Tuy nhiên, khi tôi cố thực hiện yêu cầu mạng trong công nhân, các yêu cầu có vẻ bị chặn bởi chuỗi giao diện người dùng. Dưới đây là một ví dụ giả tạo để minh họa cho vấn đề:

base.js:

var worker = new Worker("/worker.js"); 

setTimeout(function() { 
    console.log("begin blocking"); 
    var startDt = new Date(); 
    var blockPeriod = 5000; 
    var a; 
    // Obviously we'd never actually do this, but this while loop 
    // is a convenient way to create the problem case (a blocked main 
    // thread). 
    while ((new Date() - startDt) < blockPeriod) { 
     a = 0; 
    } 
    console.log("stop blocking"); 
}, 3000); 

worker.js:

var requestInterval = 1000; 

var sendRequest = function() { 
    console.log("Send request interval"); 

    var request = new XMLHttpRequest(); 
    request.open("GET", "/ping", true); 

    request.onload = function() { 
     if (request.status === 200){ 
      console.log(request.responseText) 
     } else { 
      console.log(request.status) 
     } 
    }; 

    request.onerror = function() { 
     console.log("error") 
    }; 

    request.send(); 

    setTimeout(sendRequest, requestInterval); 
} 

sendRequest(); 

Kết quả tôi nhìn thấy được mà chúng ta thấy các yêu cầu HTTP thành công cho ba giây, cho đến khi chặn bắt đầu. Tại thời điểm này, chúng ta không thấy bất cứ điều gì đăng nhập vào giao diện điều khiển cho đến khi kết thúc chặn, lúc này chúng ta thấy năm "Gửi khoảng yêu cầu" s theo sau là 5 bản ghi của phản ứng, như vậy:

Send request interval 
{"pong": true} 
Send request interval 
{"pong": true} 
Send request interval 
{"pong": true} 
Send request interval 
{"pong": true} 
begin blocking 
stop blocking 
5x Send request interval 
5x {"pong": true} 
Send request interval 
{"pong": true} 

tôi cũng thấy trong nhật ký máy chủ của tôi mà không có yêu cầu nào được thực hiện trong thời gian chặn đó, thì năm yêu cầu đó đều được nhận gần như đồng thời vào cuối thời gian chặn.

Cho rằng "Gửi khoảng thời gian yêu cầu" xảy ra năm lần liên tiếp, công nhân rõ ràng là tiếp tục thực hiện: nếu không, nó sẽ không làm cho nó thông qua để xếp hàng lặp tiếp theo. Tôi cũng thấy rằng nếu tôi chặn bằng cách kích hoạt window.alert thay vì quay trong vòng lặp, tôi nhận được thông báo tường trình từ đầu sendRequest trong khoảng thời gian 1 giây và sau đó nhận thông báo nhật ký trình xử lý phản hồi trong một loạt lớn ngay khi tôi ngừng chặn.

Trong Firefox, chuỗi nền dường như dừng hoàn toàn trong trường hợp này (Tôi không nhận được cùng một loạt năm yêu cầu được xếp hàng trong khoảng thời gian bị chặn). Tuy nhiên, tôi chỉ nhắm mục tiêu Chrome trong trường hợp này (và cuối cùng tôi muốn sử dụng WebSockets mà thậm chí không hoạt động trong Firefox Workers), vì vậy tôi không thực sự quan tâm đến điều đó.

Tất cả được cộng lại với nhau, điều này khiến tôi tin rằng có một số lớp hoạt động trong Web Workers bị chặn bởi luồng sinh sản, và một số không (tôi đã thấy hành vi tương tự này với WebSockets). Cụ thể, tôi muốn biết (nếu có ai biết):

  1. Hoạt động nào của công nhân bị chặn bởi chủ đề chính trong Chrome?
  2. Có cách nào để giải quyết vấn đề này không? Tôi rất muốn có thể thiết lập một kết nối WebSocket trong một Worker, và sau đó tiếp tục PING/PONG qua lại, ngay cả khi một cái gì đó (chẳng hạn như một cảnh báo/xác nhận) không chặn thread chính.
  3. Điều này có vô nghĩa không, và tôi có đang làm điều gì đó ngu ngốc không?
+0

Vâng, nó [có vẻ như vòng lặp sự kiện không dừng] (http://stackoverflow.com/a/2734311/1048572) trong một 'alert' :-) – Bergi

+0

Bergi, cảm ơn bạn đã trả lời. Tuy nhiên, toàn bộ ý tưởng của công nhân web là họ hoạt động trong một chủ đề nền và không can thiệp vào giao diện người dùng (bobince thậm chí đề cập đến cảnh báo này trong câu trả lời bạn đã liên kết). Và đó rõ ràng là trường hợp ở một mức độ nào đó: các ví dụ tôi đã phác thảo ở trên cho thấy rằng ở một mức độ nào đó, chúng được thực hiện ngay cả khi chủ đề chính bị chặn. Câu hỏi lớn của tôi là nơi dòng nằm giữa cái gì và không bị chặn bởi chủ đề chính. – heliotrope

+0

"Không chặn không nhất thiết có nghĩa là đồng thời". Bài viết nguồn: http://code.tutsplus.com/tutorials/getting-started-with-web-workers--net-27667 – sergolius

Trả lời

4

Quan sát của bạn là chính xác. Khi chuỗi giao diện người dùng bị chặn, cuộc gọi mạng sẽ không được gửi đi.

Thậm chí tệ hơn, Chrome có hành vi tốt nhất của nhóm. Khi một công nhân đưa ra yêu cầu XHR khi chuỗi giao diện người dùng bị chặn:

  • Chrome: tất cả các yêu cầu đều được xếp hàng đợi. Trình duyệt sẽ không thực sự đưa ra các yêu cầu cho đến khi mở khóa chuỗi giao diện người dùng. Về phía cộng, thread công nhân vẫn còn miễn phí để chạy.
  • Firefox: new XMLHttpRequest() khối cho đến khi mở khóa chuỗi giao diện người dùng.
  • IE: xhr.open() khối cho đến khi mở khóa chuỗi giao diện người dùng.

Trong khi Chrome may mắn không làm cho chuỗi công việc dừng lại và chờ (mặc dù nó không nhận được bất kỳ dữ liệu nào), Firefox và IE sẽ khiến luồng công nhân phải đợi trên chuỗi giao diện người dùng khi bạn cố gắng thực hiện một yêu cầu XHR.

Không có cách nào để giải quyết vấn đề này; bạn sẽ được nhìn thấy trình duyệt để thực hiện yêu cầu nhân danh bạn. Tôi chưa thực hiện bất kỳ thử nghiệm nào với WebSockets, nhưng chúng có thể cung cấp sự kiện ngay cả khi chuỗi giao diện người dùng bị chặn. Tệ nhất, các tin nhắn đã nhận sẽ được xếp hàng cho đến khi mở khóa chuỗi giao diện người dùng.

+1

Welp, có điều đó. Trong thử nghiệm của tôi, cùng một vấn đề xảy ra với WebSockets quá (ngay cả khi kết nối đã được thiết lập, tin nhắn không thể được chuyển qua lại trong khi thread chính bị chặn). Nói chung, bạn có hiểu rõ lý do tại sao cuộc gọi mạng không được gửi đi trong khi chuỗi giao diện người dùng bị chặn không? Hoàn toàn hợp lý nếu không, chỉ tò mò ... – heliotrope

+1

Sợ tôi thực sự không thể cung cấp cho bạn lý do chính xác đằng sau lý do tại sao nó hoạt động theo cách của nó - hoặc nếu nó thậm chí còn có chủ ý. Dù sao, để giải quyết vấn đề của bạn, bạn đã cân nhắc sử dụng phương thức hộp đèn và/hoặc ['Thông báo'] (https://developer.mozilla.org/en-US/docs/Web/API/notification) thay vì 'cảnh báo() '? – josh3736

+1

Ah, vì vậy nó đi (chỉ là tò mò). Đúng, tôi đã nghĩ về nó, nhưng đáng buồn là có một vài trường hợp trong ứng dụng có liên quan mà tôi không thể tránh cảnh báo chặn (ví dụ như tham gia vào các sự kiện 'pageunload'), và tôi cũng thường tìm cách tối đa hóa khả năng phục hồi chặn không mong muốn: /. Cảm ơn bạn rất nhiều vì đã trả lời của bạn! – heliotrope

2

Trong trường hợp bất cứ ai tình cờ ngang qua đây, hành vi này được xác nhận là một lỗi (với định nghĩa lỏng lẻo của "lỗi" là "không cư xử như nó nên") trong Blink, tính đến tháng 2 năm 2015:

https://code.google.com/p/chromium/issues/detail?id=443374

+1

Sự cố vừa được cập nhật thành * Đã sửa *. Hy vọng rằng nó sẽ được tung ra sớm. – tvbusy

+0

Nó được sửa trong Chrome 62 (có sẵn dưới dạng Chrome Dev tại thời điểm này) – Erik

Các vấn đề liên quan