Ember RunLoop là gì và nó hoạt động như thế nào?


96

Tôi đang cố gắng hiểu cách hoạt động của Ember RunLoop và điều gì khiến nó được đánh dấu. Tôi đã xem tài liệu , nhưng vẫn có nhiều câu hỏi về nó. Tôi muốn hiểu rõ hơn cách RunLoop hoạt động để tôi có thể chọn phương thức thích hợp trong không gian tên của nó, khi tôi phải hoãn thực thi một số mã trong thời gian sau.

  • Khi nào thì Ember RunLoop bắt đầu. Nó phụ thuộc vào Bộ định tuyến hoặc Chế độ xem hoặc Bộ điều khiển hay thứ gì khác?
  • nó mất khoảng bao lâu (tôi biết điều này khá ngớ ngẩn khi hỏi và phụ thuộc vào nhiều thứ nhưng tôi đang tìm kiếm một ý tưởng chung, hoặc có thể nếu có thời gian tối thiểu hoặc tối đa mà một vòng chạy có thể mất)
  • RunLoop có được thực thi mọi lúc không, hay nó chỉ cho biết một khoảng thời gian từ đầu đến cuối quá trình thực thi và có thể không chạy trong một thời gian.
  • Nếu một dạng xem được tạo từ bên trong một RunLoop, liệu có đảm bảo rằng tất cả nội dung của nó sẽ đưa vào DOM vào thời điểm vòng lặp kết thúc không?

Thứ lỗi cho tôi nếu đây là những câu hỏi rất cơ bản, tôi nghĩ hiểu những điều này sẽ giúp những người như tôi sử dụng Ember tốt hơn.


5
Không có tài liệu tuyệt vời về vòng lặp chạy. Tôi sẽ cố gắng tập hợp một bản trình chiếu ngắn về nó trong tuần này.
Luke Melia

2
@LukeMelia câu hỏi này vẫn rất cần bạn quan tâm và có vẻ như một số người khác cũng đang tìm kiếm thông tin tương tự. Sẽ thật tuyệt vời nếu bạn có cơ hội chia sẻ những hiểu biết của mình về RunLoop.
Aras

Câu trả lời:


199

Cập nhật 10/9/2013: Xem hình ảnh trực quan tương tác này của vòng chạy: https://machty.s3.amazonaws.com/ember-run-loop-visual/index.html

Cập nhật 5/9/2013: tất cả các khái niệm cơ bản bên dưới vẫn được cập nhật, nhưng kể từ lần cam kết này , việc triển khai Ember Run Loop đã được tách thành một thư viện riêng biệt có tên là backburner.js , với một số khác biệt API rất nhỏ.

Trước hết, hãy đọc những điều sau:

http://blog.sproutcore.com/the-run-loop-part-1/

http://blog.sproutcore.com/the-run-loop-part-2/

Chúng không chính xác 100% đối với Ember, nhưng các khái niệm cốt lõi và động lực đằng sau RunLoop thường áp dụng cho Ember; chỉ có một số chi tiết thực hiện khác nhau. Tuy nhiên, đối với câu hỏi của bạn:

Khi nào thì Ember RunLoop bắt đầu. Nó phụ thuộc vào Bộ định tuyến hoặc Chế độ xem hoặc Bộ điều khiển hay thứ gì khác?

Tất cả các sự kiện cơ bản của người dùng (ví dụ: sự kiện bàn phím, sự kiện chuột, v.v.) sẽ kích hoạt vòng lặp chạy. Điều này đảm bảo rằng bất kỳ thay đổi nào được thực hiện đối với các thuộc tính bị ràng buộc bởi sự kiện đã chụp (chuột / bàn phím / bộ đếm thời gian / v.v.) đều được truyền tải đầy đủ trong hệ thống liên kết dữ liệu của Ember trước khi trả lại quyền điều khiển cho hệ thống. Vì vậy, di chuyển chuột của bạn, nhấn một phím, nhấp vào một nút, v.v., tất cả đều khởi chạy vòng lặp.

nó mất khoảng bao lâu (tôi biết điều này khá ngớ ngẩn khi hỏi và phụ thuộc vào nhiều thứ nhưng tôi đang tìm kiếm một ý tưởng chung, hoặc có thể nếu có thời gian tối thiểu hoặc tối đa mà một vòng chạy có thể mất)

RunLoop sẽ không bao giờ theo dõi lượng thời gian cần thiết để truyền tải tất cả các thay đổi qua hệ thống và sau đó tạm dừng RunLoop sau khi đạt đến giới hạn thời gian tối đa; thay vào đó, RunLoop sẽ luôn chạy đến hoàn thành và sẽ không dừng lại cho đến khi tất cả các bộ định thời hết hạn được gọi, các ràng buộc được truyền và có lẽ các ràng buộc của chúng được truyền, v.v. Rõ ràng, càng nhiều thay đổi cần được truyền tải từ một sự kiện duy nhất, thì RunLoop sẽ mất nhiều thời gian hơn để kết thúc. Đây là một ví dụ (khá không công bằng) về cách RunLoop có thể sa lầy với những thay đổi lan truyền so với một khung công tác khác (Backbone) không có vòng lặp chạy: http://jsfiddle.net/jashkenas/CGSd5/. Đạo đức của câu chuyện: RunLoop thực sự nhanh đối với hầu hết những thứ bạn muốn làm ở Ember và đó là nơi phần lớn sức mạnh của Ember nằm ở chỗ, nhưng nếu bạn thấy mình muốn tạo hoạt ảnh cho 30 vòng kết nối bằng Javascript ở tốc độ 60 khung hình / giây, thì có thể là những cách tốt hơn để thực hiện nó hơn là dựa vào RunLoop của Ember.

RunLoop có được thực thi mọi lúc không, hay nó chỉ cho biết một khoảng thời gian từ đầu đến cuối quá trình thực thi và có thể không chạy trong một thời gian.

Nó không được thực thi mọi lúc - nó phải trả lại quyền kiểm soát cho hệ thống vào một thời điểm nào đó, nếu không ứng dụng của bạn sẽ bị treo - nó khác với, chẳng hạn như một vòng lặp chạy trên máy chủ có while(true)và tiếp tục cho đến khi máy chủ nhận được tín hiệu để tắt ... Ember RunLoop không có điều đó while(true)mà chỉ được quay vòng để phản hồi các sự kiện của người dùng / bộ đếm thời gian.

Nếu một dạng xem được tạo từ bên trong một RunLoop, liệu có đảm bảo rằng tất cả nội dung của nó sẽ đưa vào DOM vào thời điểm vòng lặp kết thúc không?

Hãy xem nếu chúng ta có thể tìm ra điều đó. Một trong những thay đổi lớn từ SC sang Ember RunLoop là thay vì lặp đi lặp lại giữa invokeOnceinvokeLast(mà bạn thấy trong sơ đồ trong liên kết đầu tiên về RL của SproutCore), Ember cung cấp cho bạn danh sách 'hàng đợi', trong trong vòng lặp chạy, bạn có thể lập lịch các hành động (các hàm được gọi trong vòng chạy) bằng cách chỉ định hành động thuộc hàng đợi nào (ví dụ từ nguồn Ember.run.scheduleOnce('render', bindView, 'rerender');:).

Nếu bạn nhìn vào run_loop.jstrong mã nguồn, bạn sẽ thấy Ember.run.queues = ['sync', 'actions', 'destroy', 'timers'];, tuy nhiên nếu bạn mở trình gỡ lỗi JavaScript trong trình duyệt trong một ứng dụng Ember và đánh giá Ember.run.queues, bạn sẽ có được một danh sách đầy đủ hơn về hàng đợi: ["sync", "actions", "render", "afterRender", "destroy", "timers"]. Ember giữ cho cơ sở mã của họ khá mô-đun và họ làm cho mã của bạn, cũng như mã của chính nó trong một phần riêng biệt của thư viện, có thể chèn thêm hàng đợi. Trong trường hợp này, thư viện Ember Views sẽ chèn renderafterRenderxếp hàng, cụ thể là sau actionshàng đợi. Tôi sẽ hiểu tại sao điều đó có thể xảy ra trong giây lát. Đầu tiên, thuật toán RunLoop:

Thuật toán RunLoop khá giống như được mô tả trong các bài viết về vòng lặp chạy SC ở trên:

  • Bạn chạy mã của mình giữa RunLoop .begin().end(), chỉ trong Ember, thay vào đó, bạn sẽ muốn chạy mã của mình bên trong Ember.run, mã này sẽ gọi nội bộ beginendcho bạn. (Chỉ mã vòng lặp chạy nội bộ trong cơ sở mã Ember vẫn sử dụng beginend, vì vậy bạn chỉ nên gắn bó với Ember.run)
  • Sau khi end()được gọi, RunLoop sau đó chuyển sang bánh răng để truyền mọi thay đổi được thực hiện bởi đoạn mã được chuyển đến Ember.runhàm. Điều này bao gồm tuyên truyền các giá trị của thuộc tính ràng buộc, làm cho cái nhìn thay đổi đối với DOM, vv vv Thứ tự mà những hành động (ràng buộc, làm phần tử DOM, vv) được thực hiện được xác định bởi các Ember.run.queuesmảng mô tả ở trên:
  • Vòng lặp chạy sẽ bắt đầu ở hàng đợi đầu tiên, đó là sync. Nó sẽ chạy tất cả các hành động đã được lập lịch vào synchàng đợi bởi Ember.runmã. Bản thân những hành động này cũng có thể lên lịch nhiều hành động hơn được thực hiện trong cùng RunLoop này và tùy thuộc vào RunLoop để đảm bảo nó thực hiện mọi hành động cho đến khi tất cả các hàng đợi được xóa. Cách nó thực hiện là, vào cuối mỗi hàng đợi, RunLoop sẽ xem xét tất cả các hàng đợi đã được xóa trước đó và xem liệu có hành động mới nào đã được lên lịch hay không. Nếu vậy, nó phải bắt đầu ở đầu hàng đợi sớm nhất với các hành động đã lên lịch không được thực thi và loại bỏ hàng đợi, tiếp tục theo dõi các bước của nó và bắt đầu lại khi cần thiết cho đến khi tất cả hàng đợi hoàn toàn trống.

Đó là bản chất của thuật toán. Đó là cách dữ liệu bị ràng buộc được truyền qua ứng dụng. Bạn có thể mong đợi rằng một khi RunLoop chạy đến hoàn thành, tất cả dữ liệu liên kết sẽ được truyền tải đầy đủ. Vậy còn các phần tử DOM thì sao?

Thứ tự của các hàng đợi, bao gồm cả những hàng được thêm vào bởi thư viện Ember Views, rất quan trọng ở đây. Chú ý điều đó renderafterRenderđến sau sync, và action. Các synchàng đợi chứa tất cả các hành động cho tuyên truyền dữ liệu ràng buộc. ( action, sau đó, chỉ được sử dụng thưa thớt trong nguồn Ember). Dựa trên thuật toán trên, đảm bảo rằng vào thời điểm RunLoop đến renderhàng đợi, tất cả các ràng buộc dữ liệu sẽ hoàn tất đồng bộ hóa. Đây là do thiết kế: bạn sẽ không muốn thực hiện nhiệm vụ đắt đỏ là hiển thị các phần tử DOM trước đâyđồng bộ hóa các ràng buộc dữ liệu, vì điều đó có thể sẽ yêu cầu hiển thị lại các phần tử DOM với dữ liệu cập nhật - rõ ràng là một cách rất kém hiệu quả và dễ xảy ra lỗi để làm trống tất cả các hàng đợi RunLoop. Vì vậy, Ember thông qua tất cả các công việc liên kết dữ liệu mà nó có thể làm trước khi hiển thị các phần tử DOM trong renderhàng đợi.

Vì vậy, cuối cùng, để trả lời câu hỏi của bạn, vâng, bạn có thể mong đợi rằng mọi kết xuất DOM cần thiết sẽ diễn ra vào thời điểm Ember.runkết thúc. Đây là jsFiddle để chứng minh: http://jsfiddle.net/machty/6p6XJ/328/

Những điều khác cần biết về RunLoop

Quan sát viên so với Ràng buộc

Điều quan trọng cần lưu ý là Observer và Bindings, mặc dù có chức năng tương tự là phản hồi các thay đổi trong thuộc tính "đã theo dõi", hoạt động hoàn toàn khác trong ngữ cảnh của RunLoop. Sự lan truyền ràng buộc, như chúng ta đã thấy, được lên lịch vào synchàng đợi để cuối cùng được RunLoop thực thi. Mặt khác, những người quan sát sẽ kích hoạt ngay lập tức khi thuộc tính được theo dõi thay đổi mà không cần phải được lập lịch trước vào hàng đợi RunLoop. Nếu một Người quan sát và một ràng buộc tất cả "theo dõi" cùng một thuộc tính, thì người quan sát sẽ luôn được gọi sớm hơn 100% so với thời gian ràng buộc sẽ được cập nhật.

scheduleOnceEmber.run.once

Một trong những cách tăng hiệu quả lớn trong các mẫu tự động cập nhật của Ember là dựa trên thực tế là nhờ RunLoop, nhiều hành động RunLoop giống hệt nhau có thể được liên kết ("gỡ lỗi", nếu bạn muốn) thành một hành động. Nếu bạn nhìn vào run_loop.jsbên trong, bạn sẽ thấy các chức năng hỗ trợ hành vi này là các chức năng liên quan scheduleOnceEm.run.once. Sự khác biệt giữa chúng không quá quan trọng bằng việc biết chúng tồn tại và cách chúng có thể loại bỏ các hành động trùng lặp trong hàng đợi để ngăn chặn nhiều phép tính cồng kềnh, lãng phí trong vòng chạy.

Còn về bộ đếm thời gian?

Mặc dù 'bộ tính giờ' là một trong những hàng đợi mặc định được liệt kê ở trên, Ember chỉ tham chiếu đến hàng đợi trong các trường hợp thử nghiệm RunLoop của họ. Có vẻ như một hàng đợi như vậy sẽ được sử dụng trong những ngày của SproutCore dựa trên một số mô tả từ các bài viết trên về bộ hẹn giờ là thứ cuối cùng được kích hoạt. Ở Ember, timershàng đợi không được sử dụng. Thay vào đó, RunLoop có thể được tạo ra bởi một setTimeoutsự kiện được quản lý nội bộ (xem invokeLaterTimerschức năng), đủ thông minh để lặp qua tất cả các bộ hẹn giờ hiện có, kích hoạt tất cả các bộ hẹn giờ đã hết hạn, xác định bộ hẹn giờ sớm nhất trong tương lai và đặt nội bộsetTimeoutchỉ cho sự kiện đó, sẽ quay lại RunLoop khi nó kích hoạt. Cách tiếp cận này hiệu quả hơn việc đặt mỗi cuộc gọi hẹn giờ setTimeout và tự đánh thức, vì trong trường hợp này, chỉ cần thực hiện một lệnh gọi setTimeout và RunLoop đủ thông minh để kích hoạt tất cả các bộ hẹn giờ khác nhau có thể hoạt động cùng một lúc thời gian.

Tiếp tục gỡ lỗi với synchàng đợi

Đây là một đoạn trích từ vòng lặp chạy, ở giữa vòng lặp qua tất cả các hàng đợi trong vòng chạy. Lưu ý trường hợp đặc biệt đối với synchàng đợi: bởi vì synclà một hàng đợi đặc biệt dễ bay hơi, trong đó dữ liệu đang được truyền đi theo mọi hướng, Ember.beginPropertyChanges()được gọi để ngăn bất kỳ người quan sát nào bị kích hoạt, theo sau là một cuộc gọi đến Ember.endPropertyChanges. Điều này là khôn ngoan: nếu trong quá trình xóa synchàng đợi, hoàn toàn có khả năng một thuộc tính trên một đối tượng sẽ thay đổi nhiều lần trước khi nằm ở giá trị cuối cùng của nó và bạn sẽ không muốn lãng phí tài nguyên bằng cách ngay lập tức kích hoạt người quan sát cho mỗi thay đổi. .

if (queueName === 'sync') 
{
    log = Ember.LOG_BINDINGS;

    if (log) 
    {
        Ember.Logger.log('Begin: Flush Sync Queue');
    }

    Ember.beginPropertyChanges();
    Ember.tryFinally(tryable, Ember.endPropertyChanges);

    if (log) 
    { 
        Ember.Logger.log('End: Flush Sync Queue'); 
    }
} 
else 
{
   forEach.call(queue, iter);
}

Hi vọng điêu nay co ich. Tôi chắc chắn đã phải học khá nhiều chỉ để viết điều này, đó là một vấn đề quan trọng.


3
Viết rất hay! Tôi nghe tin đồn rằng điều "quan sát viên bắn ra ngay lập tức" có thể thay đổi vào một thời điểm nào đó, khiến chúng bị trì hoãn như những ràng buộc.
Jo Liss

@JoLiss yeah, tôi cảm thấy như tôi đã nghe về điều đó trong một vài tháng ... không chắc chắn nếu / khi nó sẽ làm cho nó trong.
Alexander Wallace Matchneer

1
Brendan Briggs đã có một bài thuyết trình tuyệt vời về Run Loop tại buổi họp mặt Ember.js NYC tháng 1 năm 2014. Video ở đây: youtube.com/watch?v=iCZUKFNXA0k
Luke Melia

1
Câu trả lời này là nguồn tốt nhất tôi tìm thấy về Ember Run Loop, rất tốt! Gần đây tôi đã xuất bản một hướng dẫn mở rộng về Run Loop dựa trên công việc của bạn mà tôi hy vọng sẽ mô tả chi tiết hơn về cơ chế đó. Có sẵn tại đây trên.netguru.co/ember
Kuba Niechciał
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.