Stack Reconciler (eskirgan)

Ilgari, React rendering uchun stack(stek) ma’lumotlar strukturasi(data structure)dan foydalangan. Biz bir xil tushunchani tasavvur qilayotganimizga ishonch hosil qilish uchun, stack ma’lumotlar strukturasini qisqacha muhokama qilamiz

Stack o’zi nima

Computer science’da stack — bu oxirgi kirgan birinchi chiqadi (LIFO) tamoyiliga amal qiladigan chiziqli ma’lumotlar strukturasi(linear data structure). Bu shuni anglatadiki, stack’ga qo’shilgan oxirgi element birinchi bo’lib olib tashlanadi. Stack’da ikkita asosiy amal mavjud: push va pop, ular orqali elementlarni mos ravishda stack’ning yuqori qismiga qo’shish va undan olib tashlash mumkin.

Stack’ni vertikal joylashtirilgan elementlar to’plami sifatida tasavvur qilish mumkin, bunda eng yuqori element eng oxirgi qo’shilgan element hisoblanadi. Bu yerda stack’ning uchta elementi bilan ASCII illyustratsiyasini ko’rishingiz mumkin:

-----
| 3 |
|___|
| 2 |
|___|
| 1 |
|___|

pop va push amali

Ushbu misolda, eng oxirgi qo’shilgan element — 3, stack’ning yuqorisida joylashgan. Birinchi qo’shilgan element — 1 esa stack’ning pastki qismida joylashgan.

Ushbu stack’da push amali elementni stack’ning yuqorisiga qo’shadi. Kodda bu JavaScript’da array va push metodidan foydalanib bajarilishi mumkin, quyidagicha:

const stack = [];
stack.push(1); // stack hozir [1]
stack.push(2); // stack hozir [1, 2]
stack.push(3); // stack hozir [1, 2, 3]

Pop amali esa stackning yuqori elementini olib tashlaydi. Kodda bu JavaScript’da array va pop metodidan foydalanib bajarilishi mumkin, quyidagicha:

const stack = [1, 2, 3];
const top = stack.pop(); // top hozir 3, va stack hozir [1, 2]

Ushbu misolda, pop metodi stack’ning yuqori elementini (3) olib tashlaydi va uni qaytaradi. Stack massivi hozirda qolgan elementlarni (1 va 2) o’z ichiga oladi.

React’da stack reconciler va uning muammolari

React’ning dastlabki “reconciler” algoritmi stack asosida ishlaydigan algoritm bo’lib, u eski va yangi virtual daraxtlarni taqqoslash hamda DOM’ni tegishli ravishda yangilash uchun ishlatilgan. “Stack reconciler” oddiy holatlarda yaxshi ishlagan bo’lsa-da, ilovalar o’lchami va murakkabligi ortishi bilan bir qator qiyinchiliklarni keltirib chiqardi.

Form misoli

Keling, nega bu holat yuz berganini tezkorlik bilan ko’rib chiqamiz. Buning uchun quyidagi misolni ko’rib chiqamiz, bunda bizda yangilanishlar ro’yxati bor:

  1. Keraksiz va hisoblash jihatdan og’ir komponent CPU’ni ishlatadi va render qilinadi.
  2. Foydalanuvchi input elementiga ma’lumot yozadi.
  3. Agar kiritilgan ma’lumot yaroqli(valid) bo’lsa, Button yoqiladi.
  4. Ichida Form bo’lgan komponenti state’ni saqlaydi, shuning uchun u qayta render qilinadi.

Kodda biz buni quyidagicha ifodalaymiz:

import React, { useReducer } from "react";
 
const initialState = { text: "", isValid: false };
 
function Form() {
    const [state, dispatch] = useReducer(reducer, initialState);
 
    const handleChange = (e) => {
        dispatch({ type: "handleInput", payload: e.target.value });
    };
 
    return (
        <div>
            <ExpensiveComponent />
            <input value={state.text} onChange={handleChange} />
            <Button disabled={!state.isValid}>Submit</Button>
        </div>
    );
}
 
function reducer(state, action) {
    switch (action.type) {
        case "handleInput":
            return {
                text: action.payload,
                isValid: action.payload.length > 0,
            };
        default:
            throw new Error();
    }
}

Misoldan kelib chiqadigan muammolar

Bu holda, stack reconciler’i bo’lganda ketma-ket ravishda yangilanishlarni render qilardi, ishni to’xtatish yoki kechiktirish imkoni bo’lmasdi. Agar hisoblash jihatdan og’ir komponent renderlashni blok qilsa, foydalanuvchi inputda kiritgan ma’lumotlar ekranda kechikish bilan paydo bo’ladi. Bu yomon foydalanuvchi bilan ishlash qulayligiga olib keladi, chunki tekst maydoni javobsiz bo’lib qoladi. Aksincha, agar hisoblash jihatdan og’ir komponentni render qilishini kechiktirgan holda va ushbu og’ir komponentni render qilishdan ko’ra, foydalanuvchi kiritishini yuqoriroq ustuvorlikka ega yangilanish sifatida tan olish va ekranni yangilash, ko’proq qulay bo’lar edi.

Stack reconciler’dan kelib chiqadigan muammolar

Mavjud renderlash ishidan voz kechish zarurati bor edi, agar u yuqori ustuvorlikdagi renderlash ishi, masalan, foydalanuvchi kiritishi bilan to’xtatilsa. Buning uchun React ba’zi renderlash operatsiyalari boshqa operatsiyalardan ustunroq bo’lgan ustuvorlikka ega bo’lishi kerak edi.

Yangilanishlarni ustuvorlikka(priority) ajratmasligi

Stack reconciler yangilanishlarni ustuvorlikka ajratmagan, bu esa kamroq ahamiyatga ega bo’lgan yangilanishlar ko’proq ahamiyatga ega bo’lgan yangilanishlarni blok qilib qo’yishi mumkinligini anglatardi. Masalan, past ustuvorlikdagi “tooltip” yangilanishi yuqori ustuvorlikdagi matn kiritish yangilanishini to’sib qo’yishi mumkin edi. Virtual daraxtdagi yangilanishlar stack’da qanday qabul qilingan bo’lsa, shunday tartibda amalga oshirilar edi.

React ilovasida virtual daraxtdagi yangilanishlar turli darajadagi ahamiyatlarga ega bo’lishi mumkin. Masalan, formaga input’dagi yangilanish, foydalanuvchi to’g’ridan-to’g’ri input bilan o’zaro aloqada bo’lib, uning javob berishini kutayotgani uchun, postdagi “like”lar sonini ko’rsatadigan indikator yangilanishidan ko’ra muhimroq bo’lishi mumkin.

Stack’da tartib bo’yicha yangilanishi

Stack reconciler’da yangilanishlar qabul qilingan tartibda bajarilar edi, bu kamroq ahamiyatga ega yangilanishlar ko’proq ahamiyatga ega yangilanishlarni blok qilishi mumkinligini anglatardi. Masalan, agar input yangilanishidan oldin “like”lar sonini hisoblash yangilanishi qabul qilinsa, “like”lar sonini hisoblash yangilanishi birinchi bo’lib bajariladi va form input yangilanishini blok qilishi mumkin.

Agar “like”lar sonini hisoblash yangilanishi uzoq vaqt talab qilsa (masalan, og’ir hisoblash ishlari tufayli), bu foydalanuvchi interfeysida sezilarli kechikish yoki buzilish(jank)ga olib kelishi mumkin, ayniqsa foydalanuvchi yangilanish davomida ilova bilan o’zaro aloqada bo’lsa.

Yangilanishni to’xtatish yoki bekor qila olmasligi

Stack reconciler’dagi yana bir muammo shundaki, yangilanishlarni to’xtatish yoki bekor qilish mumkin emas edi. Bu shuni anglatadiki, hatto stack reconciler yangilanish ustuvorligini bilsa ham, u yuqori ustuvorlikdagi yangilanish rejalashtirilganda ahamiyatsiz ishni to’xtatish orqali turli ustuvorliklar bilan yaxshi ishlashiga kafolat yo’q edi.

Har qanday veb-ilovada barcha yangilanishlar teng darajada muhim emas: tasodifiy kutilmagan bildirishnoma chiqishi tugmani bosilishiga javobichalik muhim emas, chunki tugmani bosishni foydalanuvchi xohlab qiladi, va bu darhol javob berishni talab qiladi, holbuki bildirishnoma esa hatto kutilmagan va istalmagan bo’lishi mumkin.

Stack reconciler’da yangilanishlarni to’xtatish yoki bekor qilish mumkin emas edi, bu shuni anglatadiki, keraksiz yangilanishlar, masalan “toast” ko’rsatish kabi, ba’zan foydalanuvchining interaktivligi evaziga amalga oshirilgan. Bu virtual daraxt va DOM’da keraksiz ishlar bajarilishiga olib kelishi mumkin edi, bu esa ilovaning performance’iga salbiy ta’sir ko’rsatardi.

Stack reconciler ilovalar kattalashgani va murakkablashgani sari ko’plab qiyinchiliklarni yuzaga keltirdi. Asosiy qiyinchiliklar interfeysning kechikishi va foydalanuvchi interfeyslarining sekin javob berishi bilan bog’liq edi. Bu qiyinchiliklarni bartaraf etish uchun React jamoasi boshqa ma’lumotlar strukturasiga asoslangan “Fiber reconciler” deb nomlanuvchi yangi reconciler ishlab chiqdilar. Keyingi bo’limda ushbu ma’lumotlar strukturasini o’rganamiz.