동기식 API를 사용한 반응 캐시, 타임 슬라이싱 및 페치

글쎄, 올해는 React의 해인 것 같습니다. 16.7-alpha.0 — Hooks와 함께 제공되는 새로운 킬러 기능에 대해 들어 보셨을 것입니다. Time Slicing 또는 Suspense와 같은 훌륭하고 멋진 것들에 대해 들어봤을 것입니다.

이 기사는 새로운 기능 중 일부를 사용하는 방법을 설명하는 것이 아니라 기능이 어떻게 만들어 졌는지 증명하는 것을 목표로합니다. 우리가 무엇을하고 있는지 이해하기 위해.

또한이 기능을 발견 한 방식으로 작성되었습니다. 생각했던 방식이 아닐 수도 있지만 이것이 제가 요점을 얻는 방법입니다.

읽는 동안 찾을 내용 :

  • 비동기 JavaScript 및 이벤트 루프
  • 예를 들어 React의 대수 효과
  • 섬유 및 반응 단계

왜이 게시물을 작성 했습니까?

이 게시물을 작성하고 싶었던 것은 동기식 API를 사용하여 비동기 작업을 사용할 수있는 특별하고 실험적인 기능이었습니다.

const bulbasaur = ApiResource.read ()?… 무엇입니까? 동기?!

react-cache 라이브러리는 동기 API와 함께 비동기 작업을 사용하는 기능을 만듭니다. 이것은 React가 어떻게 작동하는지 배우고 싶었던 기능입니다. 이 라이브러리에서 Dan Abramov와 Andrew Clark이 발표 한 프레젠테이션은 다음과 같습니다.

어떻게 가능합니까? 동기식 호출을 사용하여 원격 데이터를 얻는 방법은 무엇입니까?

이 예제를 자세히 살펴보고 react-cache가 이러한 기능을 구현하는 방법을 이해하고 작동 방법을 알아 봅니다. 이 이야기는 섬유 아키텍처로 시작합니다.

자바 스크립트 작업 제어

파이버 아키텍처를 통해 React는 작업 실행을 제어 할 수 있습니다. React가 겪은 여러 문제를 해결하기 위해 만들어졌습니다. 내 관심을 끈 두 가지가 있습니다.

  • 데이터 페치에 대한 사용자 입력과 같은 특정 이벤트보다 우선 순위 지정
  • 비동기식으로 분할하여 React 계산을 통해 주요 스레드 가용성을 유지하고 긴 렌더링 프로세스 중에이를 차단하지 않습니다.

JavaScript 애플리케이션 내에서 상태 변경 (리 액트뿐만 아니라)을 트리거하는 모든 것은 비동기 작업으로 인한 것입니다. 여기에는 setTimeout, fetch 및 이벤트 리스너가 포함됩니다.

비동기 작업은 여러 JavaScript 핵심 개념을 통해 관리됩니다.

  • 작업 (마이크로, 매크로, 렌더링 등)
  • 이벤트 루프
  • 콜 스택

이러한 개념에 익숙하지 않은 경우 Jake Archibald의이 비디오를 살펴 보시기 바랍니다.

파이버 덕분에 사용자 입력은 페치 호출과 같은 다른 비동기 작업보다 먼저 해결됩니다.

이것이 어떻게 가능합니까?

글쎄, 위의 Archibald의 이야기는 이벤트 루프의 작동 방식에 대한 나의 학습 경로의 첫 포장 돌이었습니다. 그는 Promise API를 통해 생성 된 마이크로 작업은 다음 매크로 작업 전에 실행되고 플러시된다고 말합니다. 이 프로세스는 setTimeout과 같은 콜백 기반 메소드를 사용합니다.

따라서“사용자 입력과 데이터 가져 오기”비교를 기억한다면 팀이 onChange 해결 ​​후 가져 오기 해결을 어떻게 했습니까?

이러한 개념은 WhatWG / HTML5 / Ecma-262와 같은 사양에 맞지 않으며 브라우저 나 JS 엔진과 같은 다른 곳에서 제공되지 않습니다.

내 말은, setTimeout 후 Promise를 어떻게 해결해야합니까?

이것은 나에게 절대적으로 미쳤다고 들었고 그것이 어떻게 작동하는지에 대한 아이디어를 얻는 것은 정말로 어려웠습니다. 사실은 더 높은 수준에서 발생합니다.

나중에 나는 React Rally에서 Brandon Dail의 놀라운 이야기를 보았습니다. React 파이버 아키텍처 덕분에 출시 된 새로운 Time Slicing 및 Suspense 기능이 제공됩니다.

Dail에 따르면, 파이버는 스택의 각 항목을 파이버라고하는 일반적인 JavaScript 호출 스택과 같습니다. 함수 (+ 메타 데이터)를 나타내는 프레임에 의존하는 것은 콜 스택과 다릅니다. 오히려 섬유는 구성 요소 (+ 메타 데이터)를 나타냅니다. 광섬유를 모든 것을 알고있는 컴포넌트 주위의 거대한 상자로 보자.

이 두 개념 사이에는 중요한 차이점이 있습니다.

우선, 콜 스택은 JavaScript 코드를 구동하는 기본 부분 위에 구축 된 기능입니다. 모든 JavaScript 함수 호출을 스택하고 자체적으로 실행하는 것을 목표로합니다. 함수를 호출 할 때마다 스택에 추가됩니다. 콜 스택이 없으면 명확하고 자세한 오류 스택 추적을 수행 할 수 없습니다. 그리고 자바 스크립트 코드에서 콜 스택에 접근 할 수 없으므로 제어하기가 매우 어렵고 불가능합니다.

반면에 섬유 스택과 같은 섬유는 동일한 개념을 나타내지 만 JavaScript 코드에 내장되어 있습니다. 가장 작은 단위는 기능이 아니라 구성 요소입니다. 실제로는 JavaScript 유니버스에서 실행됩니다.

파이버 아키텍처가 JavaScript로 완전히 구축되었다는 사실은이를 사용하고 액세스하여 수정할 수 있음을 의미합니다. 표준 JavaScript를 사용하여 작업 할 수 있습니다.

잘못된 방향으로 나를 이끌었던 것은 React가 JavaScript가 작동하는 내부 방식을 차단하기 위해 해결 방법을 사용하고 있다고 생각했습니다. 그렇지 않습니다. 파이버는 단순히 React 구성 요소에 대한 정보를 소유하고 수명주기와 상호 작용할 수있는 JavaScript 객체입니다. React 내부 기능에 대해서만 작동 할 수 있습니다.

콜백 작업 전에 페치 마이크로 태스크 해결을 실행해야한다고 말하는 것과 같이 JavaScript 작동 방식을 재정의하는 것이 아닙니다. 다른 라이프 사이클 메소드 호출을 방해하는 것과 같이 특정 컨텍스트에서 어떤 React 메소드를 호출해야하는지에 대해 더 자세히 설명합니다.

어이 기다려! 섬유가 React 앱의 모든 것을 절대적으로 제어 할 수 있다고 말합니까? 그러나 컴포넌트가 어떻게 React에게 아무것도하지 말라고 말할 수 있습니까?

대수 효과, 예. 그러나 JavaScript에서는

광섬유 아키텍처 덕분에 React는 컴포넌트를 제어하고 컴포넌트가 실행 중인지 알 수 있습니다. 이제 누락 된 것은 React에게 특정 구성 요소에 대해 변경된 사항이 있음을 알리는 방법이므로이 변경 사항을 처리합니다.

대수 효과가 게임에 들어가는 곳입니다.

대수 효과는 JavaScript에 존재하는 것이 아닙니다. 나는 그들이 더 높은 수준의 설명으로 무엇을 설명하려고 노력할 것입니다.

대수 효과는 디스패처와 같은 정보를 어딘가에 보낼 수있는 개념입니다. 아이디어는 부모 함수가 계산을 처리 할 수 ​​있도록 현재 실행중인 함수를 정확한 위치에서 중단시키는 특정 함수를 호출하는 것입니다. 부모 계산이 끝나면 정보가 전송 된 초기 위치로 프로그램을 다시 시작할 수 있습니다.

OCaml 또는 Eff와 같은 일부 언어는 이러한 기능을 기본적으로 활용합니다. 구현 세부 사항은 부모에만 의존하기 때문에 이것은 매우 흥미로운 추상화입니다.

JavaScript에서 그러한 기능을 갖는 것이 멋지지 않습니까?

React 팀은 JavaScript try / catch 블록을 처리하는 React 컨텍스트에서 유사한 접근 방식을 작성했습니다. 데일에 따르면, JavaScript에서 가장 가까운 개념이라고합니다.

무언가를 던지면 어딘가에 부모에게 정보를 보낼 수 있습니다. 정보를 잡은 첫 번째 부모는 정보를 처리하고 계산할 수 있습니다.

예는 천 단어보다 낫다

동기식 API를 사용하여 Bulbasaur를 가져 오려고 시도하는 다음 코드를 상상해보십시오.

이 코드는 동기식 API를 사용하여 데이터를 가져 오는 것이 일반적이지 않기 때문에 이상 할 수 있습니다. customFetch 함수 구현을 살펴 보겠습니다.

아 잠깐만! 이것은 절대로 가져 오기처럼 보이지 않습니다! 이 기능의 목표를 전혀 얻지 못했습니다…

구성 요소 주위에 무언가가 있다고 상상해 봅시다.

코드를 읽는 데 시간이 걸립니다.

이제 customFetch 구현으로 넘어 갑시다.

이전 스 니펫에서 중요한 부분은 try / catch 블록입니다.

다음과 같은 다양한 코드를 통해 발생하는 상황을 요약 해 보겠습니다.

  • 포켓몬 컴포넌트는 customFetch 메소드를 호출합니다.
  • customFetch 메소드는 내부 캐시를 읽으려고 시도하지만 비어 있습니다. 그래서 그것은 대수적 효과 인 어딘가에 무언가를 던집니다.
  • 파이버 상위는 해당 정보를 잡아서 처리하고 데이터를 가져옵니다. 그런 다음 customFetch 캐시를 데이터로 채 웁니다.
  • 다시 렌더링은 Component (args)에서 발생하고 이제 customFetch 캐시가 가득 찼습니다. 이제 동기 API를 사용하여 컴포넌트에서 데이터를 사용할 수 있습니다.

react-cache 구현 세부 사항을보고 다른 발생을 확인하십시오.

이 과정에서 무언가주의를 끌었을 수 있습니다. 렌더가 두 번 호출되었습니다. 하나는 구성 요소를 다시 시작하는 중 오류를 발생시키고 (구성 요소를 일시 중지하는) 다른 하나는 데이터를 가져 오는 것입니다. React를 사용하면 여러 기능을 호출 할 수 있습니다. 순전히 기능 일뿐입니다. 부작용은 없습니다.

무엇을 기다립니다? 렌더링에 부작용이 없습니까? DOM은 어떻습니까?

반응 단계

오랫동안 React와 함께 일한 적이 있다면 여러 번 다시 렌더링하는 것이 좋지 않다고 들었을 것입니다. 파이버 아키텍처 이전에는 렌더링 함수를 호출 할 때마다 React는 내부 계산을 수행 한 다음 그에 따라 DOM을 수정했습니다. 예를 들어, setState를 통해 렌더링 함수를 호출 할 때 발생했습니다. 프로세스가 인라인되었습니다.

setState → 렌더러 → 가상 노드 비교 → DOM 노드 업데이트

섬유를 다루는 과정은 약간 다릅니다. 고성능 DOM 수정을 허용하는 대기열 및 배치 개념을 도입했습니다.

아이디어는 매우 간단합니다. 우리는 스크린이 초당 60 프레임을 실행할 수 있다고 가정합니다. 이 가정과 사용 가능한 JavaScript 함수를 사용하면 ~ 16.7ms마다 일부 계산 및 DOM 수정이 가능합니다. 파이버를 사용하면 React는 여러 수정을 대기열에 넣고 초당 약 60 번 커밋 할 수 있습니다.

이러한 종류의 수정을 통해 React는 고유 한 장점과 특성을 가진 세 단계로 나눌 수있었습니다.

반응 단계에 관한 Dan Abramov
  • 렌더 단계는 순수하고 결정 론적입니다. 부작용이 없으며 구성 된 다른 기능을 여러 번 호출 할 수 있습니다. 렌더링 단계는 중단 가능합니다. 일시 중지 모드 인 렌더링 기능이 아니라 전체 단계
  • 사전 커밋 문구는 읽기 모드에서 스크롤바 위치와 같은 실제 DOM 상태에 대한 액세스를 제공하는 것을 목표로합니다.
  • 커밋 단계는 실제로 DOM을 수정하며 인터럽트 할 수 없습니다. 해당 단계에서는 반응을 일시 중지 할 수 없습니다.

이 세 단계는 Time Slicing 기능을 도입했습니다. React는 렌더 단계에서 두 구성 요소 함수 호출 사이에서 일시 중지하고 필요한 경우 해당 단계를 다시 시작할 수 있습니다.

파이버에서는 렌더는 내부 상태를 기준으로 사용 가능한 최신 컴포넌트 표시를 가져 와서 일부 비교를 수행하고 React가 DOM을 변경해야하는지 여부를 알기위한 것입니다. 커밋 수정이 필요한 경우 "작업 진행 중"대기열에 수정 사항이 추가됩니다.

React 팀은 React Concurrent (Time Slicing + Suspense) 및 파이버 아키텍처 덕분에 성능이 크게 향상되었습니다. 이벤트 우선 순위 지정 및 동시성과 같은 다양한 브라우저 문제를 해결하기위한 해결 방법을 만들었습니다.

우리가 물러 서면 그들이 보여준 것이 아닌가? 우선 순위 지정은 브라우저 및 프론트 엔드 프레임 워크의 새로운 과제로 보입니다.

다른 팀들도 실제 최신 상태를 개선하고 미래의 API를 제안하기 위해 노력하고 있습니다. 이것이 구글의 취향입니다 :