Ember RunLoop이란 무엇이며 어떻게 작동합니까?


96

Ember RunLoop의 작동 원리와 작동 원리를 이해하려고합니다. 나는 보았다문서를 여전히 많은 질문이 있습니다. 나중에 일부 코드의 실행을 연기해야 ​​할 때 이름 공간 내에서 적절한 방법을 선택할 수 있도록 RunLoop의 작동 방식을 더 잘 이해하고 싶습니다.

  • Ember RunLoop은 언제 시작됩니다. 라우터, 뷰 또는 컨트롤러 또는 다른 것에 의존합니까?
  • 대략 얼마나 걸립니까?
  • RunLoop이 항상 실행되고 있는지 아니면 실행 시작부터 끝까지의 기간을 나타내는 것일뿐 일정 시간 동안 실행되지 않을 수 있습니다.
  • 하나의 RunLoop 내에서 뷰가 생성되는 경우 루프가 종료 될 때 모든 콘텐츠가 DOM으로 들어가는 것이 보장됩니까?

이것이 매우 기본적인 질문이라면 저를 용서하십시오, 이것들을 이해하면 저와 같은 멍청이가 Ember를 더 잘 사용하는 데 도움이 될 것이라고 생각합니다.


5
런 루프에 대한 훌륭한 문서는 없습니다. 이번 주에 짧은 슬라이드 데크를 만들어 보겠습니다.
Luke Melia

2
@LukeMelia이 질문은 여전히 ​​귀하의 관심이 절실히 필요하며 다른 사람들이 동일한 정보를 찾고있는 것 같습니다. 기회가 있다면 RunLoop에 대한 통찰력을 공유하는 것이 좋습니다.
Aras

답변:


199

2013 년 10 월 9 일 업데이트 : 실행 루프의 대화 형 시각화를 확인하십시오. https://machty.s3.amazonaws.com/ember-run-loop-visual/index.html

2013 년 5 월 9 일 업데이트 : 아래의 모든 기본 개념은 여전히 ​​최신 상태이지만 이 커밋 시점 에서 Ember Run Loop 구현은 매우 사소한 API 차이가있는 backburner.js 라는 별도의 라이브러리로 분리되었습니다 .

먼저 다음을 읽으십시오.

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

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

Ember에게 100 % 정확하지는 않지만 RunLoop의 핵심 개념과 동기는 여전히 일반적으로 Ember에 적용됩니다. 일부 구현 세부 정보 만 다릅니다. 그러나 귀하의 질문에 대해 :

Ember RunLoop은 언제 시작됩니다. 라우터, 뷰 또는 컨트롤러 또는 다른 것에 의존합니까?

모든 기본 사용자 이벤트 (예 : 키보드 이벤트, 마우스 이벤트 등)는 런 루프를 발생시킵니다. 이를 통해 캡처 된 (마우스 / 키보드 / 타이머 / 기타) 이벤트에 의해 바인딩 된 속성에 대한 변경 사항이 시스템에 제어권을 다시 반환하기 전에 Ember의 데이터 바인딩 시스템 전체에 완전히 전파됩니다. 따라서 마우스 이동, 키 누르기, 버튼 클릭 등은 모두 런 루프를 시작합니다.

대략 얼마나 걸립니까?

어떤 시점에서도 RunLoop은 시스템을 통해 모든 변경 사항을 전파하고 최대 시간 제한에 도달 한 후 RunLoop을 중지하는 데 걸리는 시간을 추적하지 않습니다. 오히려 RunLoop 항상 완료 될 때까지 실행되며, 모든 만료 된 타이머가 호출 될 때까지 멈추지 않을 것입니다, 바인딩 전파, 그리고 아마도 자신의 바인딩 등 전파. 분명히 단일 이벤트에서 전파해야하는 변경 사항이 많을수록 RunLoop이 완료되는 데 더 오래 걸립니다. 다음은 RunLoop이 실행 루프가없는 다른 프레임 워크 (Backbone)에 비해 변경 사항 전파로 인해 RunLoop이 수렁에 빠질 수있는 방법에 대한 (매우 불공평 한) 예입니다 : http://jsfiddle.net/jashkenas/CGSd5/. 이야기의 도덕 : RunLoop은 Ember에서하고 싶은 대부분의 작업에 대해 정말 빠르며 Ember의 강력한 힘이있는 곳입니다.하지만 자바 스크립트로 30 개의 원을 초당 60 프레임으로 애니메이션하고 싶다면 Ember의 RunLoop에 의존하는 것보다 더 나은 방법 일 수 있습니다.

RunLoop이 항상 실행되고 있는지 아니면 실행 시작부터 끝까지의 기간을 나타내는 것일뿐 일정 시간 동안 실행되지 않을 수 있습니다.

항상 실행되지 않습니다 -이 말에서 다르다, 실행 루프를 A가 들어있는 서버에 - 그것은 어떤 점에서 또는 다른 앱을 중지 할 시스템에 대한 제어 등을 반환하는 while(true)때까지 무한대에 간다 서버는 종료 신호를받습니다. Ember RunLoop에는 그런 while(true)것이 없지만 사용자 / 타이머 이벤트에 대한 응답으로 만 작동됩니다.

하나의 RunLoop 내에서 뷰가 생성되는 경우 루프가 종료 될 때 모든 콘텐츠가 DOM으로 들어가는 것이 보장됩니까?

우리가 그것을 알아낼 수 있는지 봅시다. SC에서 Ember RunLoop으로의 큰 변화 중 하나는 (SproutCore의 RL에 대한 첫 번째 링크의 다이어그램에서 볼 수있는) invokeOnce와 사이를 앞뒤로 반복하는 대신 invokeLastEmber가 '대기열'목록을 제공한다는 것입니다. 실행 루프가 진행되는 동안 작업이 속한 대기열을 지정하여 작업 (실행 루프 중에 호출되는 함수)을 예약 할 수 있습니다 (출처 :) Ember.run.scheduleOnce('render', bindView, 'rerender');.

당신이 보면 run_loop.js소스 코드에서, 당신은 볼 Ember.run.queues = ['sync', 'actions', 'destroy', 'timers'];당신이 엠버 응용 프로그램의 브라우저에서 자바 스크립트 디버거를 열고 평가 아직 있다면, Ember.run.queues당신은 큐의보다 완전한 목록을 얻을 : ["sync", "actions", "render", "afterRender", "destroy", "timers"]. Ember는 코드베이스를 매우 모듈 식으로 유지하며, 라이브러리의 별도 부분에있는 자체 코드뿐만 아니라 더 많은 대기열을 삽입 할 수 있도록합니다. 이 경우 Ember Views 라이브러리 는 특히 대기열 뒤에 삽입 render하고 afterRender대기열에 넣습니다 actions. 그 이유를 곧 알게 될 것입니다. 첫째, RunLoop 알고리즘 :

RunLoop 알고리즘은 위의 SC 실행 루프 문서에서 설명한 것과 거의 같습니다.

  • 당신은 RunLoop 사이에 코드를 실행 .begin()하고 .end(), 단지 엠버에서 대신 내에서 코드를 실행할 수 있습니다 Ember.run내부적으로 호출 할 것이다, begin그리고 end당신을. (Ember 코드베이스의 내부 실행 루프 코드 만 여전히 begin및을 사용하므로을 계속 end사용해야합니다. Ember.run)
  • end()이 호출 된 후 RunLoop은 Ember.run함수에 전달 된 코드 덩어리에 의해 만들어진 모든 단일 변경 사항을 전파하기 위해 작동합니다. 여기에는 바인딩 된 속성의 값 전파, DOM에 대한 뷰 변경 렌더링 등이 포함됩니다. 이러한 작업 (바인딩, DOM 요소 렌더링 등)이 수행되는 순서는 Ember.run.queues위에서 설명한 배열에 의해 결정됩니다 .
  • 실행 루프는 첫 번째 대기열 인 sync. 코드에 sync의해 대기열에 예약 된 모든 작업을 실행합니다 Ember.run. 이러한 작업은 동일한 RunLoop 동안 수행 할 더 많은 작업을 자체적으로 예약 할 수 있으며, 모든 대기열이 플러시 될 때까지 모든 작업을 수행하도록하는 것은 RunLoop의 몫입니다. 이 작업을 수행하는 방법은 모든 대기열의 끝에서 RunLoop이 이전에 플러시 된 모든 대기열을 살펴보고 새 작업이 예약되었는지 확인하는 것입니다. 그렇다면, 수행되지 않은 예약 된 작업으로 가장 빠른 대기열의 시작 부분에서 시작하고 대기열을 플러시하고 단계를 계속 추적하고 모든 대기열이 완전히 비워 질 때까지 필요할 때 다시 시작해야합니다.

이것이 알고리즘의 본질입니다. 이것이 바인딩 된 데이터가 앱을 통해 전파되는 방법입니다. RunLoop이 완료 될 때까지 실행되면 바인딩 된 모든 데이터가 완전히 전파 될 것으로 예상 할 수 있습니다. 그렇다면 DOM 요소는 어떻습니까?

Ember Views 라이브러리에 추가 된 대기열을 포함하여 대기열의 순서는 여기에서 중요합니다. 통지 renderafterRender후 올 sync하고 action. sync큐가 결합 된 데이터를 전파하기위한 모든 조치를 포함합니다. ( action그 후에는 Ember 소스에서 드물게 사용됩니다). 위의 알고리즘을 기반으로 RunLoop이 render큐에 도달 할 때 까지 모든 데이터 바인딩이 동기화를 완료 할 것입니다. 이것은 의도적으로 설계된 것입니다. 전에 DOM 요소를 렌더링하는 값 비싼 작업을 수행하고 싶지 않을 것입니다.데이터 바인딩을 동기화하려면 업데이트 된 데이터로 DOM 요소를 다시 렌더링해야 할 가능성이 높기 때문에 모든 RunLoop 큐를 비우는 매우 비효율적이고 오류가 발생하기 쉬운 방법입니다. 따라서 Ember는 render대기열 에서 DOM 요소를 렌더링하기 전에 가능한 모든 데이터 바인딩 작업을 지능적으로 처리합니다 .

따라서 마지막으로 질문에 대답하기 위해 예, 필요한 DOM 렌더링이 시간 Ember.run이 끝날 때까지 수행 될 것으로 예상 할 수 있습니다 . 다음은 시연 할 jsFiddle입니다. http://jsfiddle.net/machty/6p6XJ/328/

RunLoop에 대해 알아야 할 기타 사항

관찰자 대 바인딩

관찰자 및 바인딩은 "감시 된"속성의 변경 사항에 응답하는 유사한 기능을 갖지만 RunLoop의 컨텍스트에서 완전히 다르게 작동한다는 점에 유의해야합니다. 우리가 본 것처럼 바인딩 전파는 sync대기열에 예약되어 결국 RunLoop에 의해 실행됩니다. 반면에 관찰자는 먼저 RunLoop 대기열에 예약 할 필요없이 감시 된 속성이 변경되면 즉시 실행됩니다. 관찰자와 바인딩이 모두 동일한 속성을 "감시"하면 관찰자는 바인딩이 업데이트되기 전에 항상 100 % 호출됩니다.

scheduleOnceEmber.run.once

Ember의 자동 업데이트 템플릿의 큰 효율성 향상 중 하나는 RunLoop 덕분에 여러 개의 동일한 RunLoop 작업을 단일 작업으로 통합 ( "디 바운스") 할 수 있다는 사실에 기반합니다. run_loop.js내부 를 살펴보면 이 동작을 촉진하는 기능이 관련 기능 scheduleOnceEm.run.once. 그들 사이의 차이점은 그들이 존재한다는 것을 아는 것만 큼 중요하지 않으며 실행 루프 동안 많은 부풀고 낭비적인 계산을 방지하기 위해 대기열에서 중복 작업을 버릴 수있는 방법입니다.

타이머는 어떻습니까?

'타이머'가 위에 나열된 기본 대기열 중 하나이지만 Ember는 RunLoop 테스트 케이스의 대기열 만 참조합니다. 이러한 대기열은 타이머가 마지막으로 실행되는 것에 대한 위 기사의 설명 중 일부를 기반으로 SproutCore 시대에 사용되었을 것으로 보입니다. Ember에서는 timers대기열이 사용되지 않습니다. 대신 RunLoop은 내부적으로 관리되는 setTimeout이벤트 ( invokeLaterTimers함수 참조)에 의해 구동 될 수 있습니다.이 이벤트 는 기존의 모든 타이머를 반복하고 만료 된 모든 타이머를 실행하고 가장 빠른 미래 타이머를 결정하고 내부setTimeout해당 이벤트에만 해당되며 실행시 RunLoop이 다시 회전합니다. 이 접근 방식은 각 타이머가 setTimeout을 호출하고 스스로 깨우는 것보다 더 효율적입니다.이 경우에는 setTimeout 호출을 한 번만 수행하면되고 RunLoop은 동시에 작동 할 수있는 모든 다른 타이머를 실행할 수있을만큼 똑똑합니다 시각.

sync대기열을 사용한 추가 디 바운싱

다음은 런 루프의 모든 큐를 통과하는 루프 중간에있는 런 루프의 스 니펫입니다. sync큐 의 특별한 경우에 유의하십시오 . sync데이터가 모든 방향으로 전파되는 특히 휘발성 큐 Ember.beginPropertyChanges()가 호출되어 관찰자가 실행되는 것을 방지하기 위해 호출되고 Ember.endPropertyChanges. 이것은 현명합니다. sync큐 를 플러시하는 과정 에서 객체의 속성이 최종 값에 도달하기 전에 여러 번 변경 될 가능성이 전적으로 가능하며 매 변경마다 관찰자를 즉시 ​​실행하여 리소스를 낭비하고 싶지 않을 것입니다. .

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

도움이 되었기를 바랍니다. 저는이 글을 쓰기 위해서 확실히 꽤 많은 것을 배워야했습니다. 그것은 일종의 요점이었습니다.


3
훌륭한 글! 나는 "관찰자들이 즉시 발사"하는 것이 어떤 시점에서 바뀔 수 있다는 소문을 듣습니다.
Jo Liss

그래 @JoLiss, 나는 ... 몇 달 동안 그것에 대해 들어 본 것 같은 느낌이 확실하지가 그것을 만들 수 있습니다 때 경우 /.
알렉산더 월러스 Matchneer

1
Brendan Briggs는 2014 년 1 월 Ember.js NYC 밋업에서 Run Loop에 대한 멋진 프레젠테이션을했습니다. 비디오 : youtube.com/watch?v=iCZUKFNXA0k
Luke Melia

1
이 답변은 Ember Run Loop에 대해 찾은 최고의 리소스였습니다. 나는 최근에 당신의 작업을 기반으로하는 Run Loop에 대한 광범위한 튜토리얼을 발표했는데, 그 메커니즘에 대한 더 자세한 설명이 있기를 바랍니다. 여기에서 확인
Kuba Niechciał 2015 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.