Maonyesho ya. Programu za mtandao za kisasa zinaweza kujisikia ngumu. Wakati mwingine, kazi moja tu ya JavaScript inayotumika kwa muda mrefu ni ya kutosha kusafisha interface, kuacha watumiaji wasiwasi na wasiwasi kama programu bado inafanya kazi au imefungwa. Inatoa njia ndogo lakini yenye nguvu: Inakuwezesha mtumiaji kuacha utekelezaji, kutoa kivinjari nafasi ya kushughulikia kazi muhimu zaidi (kama vile click au kuandika), na kisha kuendelea mahali walipoacha. , kuchunguza workarounds zamani, na kuona jinsi kufanya maisha kuwa rahisi. Mapendekezo ya Task Scheduling API Kuanzisha mlinzi juu ya mlinzi ( Main Thread scheduler.yield() Jinsi ya kuondoa umwagiliaji (yield)? Kwa hiyo, nini ni Hii ni mbinu ya interface kutoka kwa mpya Njia hii inakuwezesha, kama mwanzilishi, kusimamisha utekelezaji wako wa JavaScript na kurejesha udhibiti tena - hivyo inaweza kushughulikia kazi nyingine muhimu zinazoendelea, kama vile maingiliano ya mtumiaji, clicks, kuandika, nk, na kisha kuendelea utekelezaji kutoka ambapo umekoma. Kwa mfano, unahitaji kuwaambia browser: scheduler.yield() Mipango ya Mipango ya programu ya API. Main Thread scheduler.yield() "Kwa kusubiri, kupumzika, hebu kusimama kazi ya sasa na kuzingatia kazi nyingine isiyo na maana au muhimu zaidi.Kama umefanya, kurudi na kuendelea kutekeleza kutoka mahali tulipoacha." "Kwa kusubiri, kupumzika, hebu kusimama kazi ya sasa na kuzingatia kazi nyingine isiyo na maana au muhimu zaidi.Kama umefanya, kurudi na kuendelea kutekeleza kutoka mahali tulipoacha." Hii inafanya ukurasa wako wa kukabiliana zaidi, hasa wakati wa kufanya kazi ndefu au ngumu za JavaScript. Pia inaweza kusaidia kuboresha takwimu kama - ambayo ni juu ya haraka ambayo kivinjari kinakabiliwa na kuingia kwa mtumiaji. Jinsi ya kufanya mazoezi ya Next Paint (INP) ya terminology. Kabla ya kujifunza zaidi, hebu tuendelee haraka juu ya maneno machache ya msingi ambayo yatatumika katika makala nzima. Main Thread - Hii ni mahali muhimu ambapo kivinjari kinafanya kazi yake nyingi. Inashughulikia rendering, layout, na huendesha sehemu kubwa ya msimbo wako wa JavaScript. Jukumu la muda mrefu - Hii ni jukumu lolote la JavaScript ambalo linahifadhi Mstari Mkuu kwa muda mrefu sana, kwa kawaida zaidi ya milliseconds 50. Tarehe ya kuzuia - Ni operesheni ya synchronous kwenye Mstari Mkuu ambayo inakadiriana na kivinjari cha kusindika mambo mengine muhimu, kama kujibu kwa kubonyeza au kuboresha UI. Kwa kawaida, kazi ndefu ni kuzuia kazi. kwa tatizo hilo. Ili kuelewa uzuri wa , kwanza unahitaji kuelewa tatizo gani inajaribu kutatua. JavaScript inafanya kazi kwenye thread moja. Hiyo inamaanisha inaweza kufanya kitu kimoja tu kwa wakati. Ikiwa msimbo wako unahifadhi thread, kila kitu kingine - rendering, bonyeza kifungo, kuingia kuandika inahitaji kusubiri. Katika ulimwengu mzuri, utakuwa daima kugawana kazi ngumu katika vipande vidogo. Lakini ukweli ni mbaya. Unashughulikia na msimbo wa zamani, maandiko ya tatu, au hesabu ngumu isiyohitajika. Na wakati huo huo, watumiaji huwa na kuruhusiwa na kuruhusiwa kuruhusiwa kurasa. scheduler.yield() Mfano wa utekelezaji wa JavaScript. Kama mchakato wa haraka, hapa ni chati ya mfano wa jinsi JavaScript inavyoshughulikia kazi - kwa maneno mengine, jinsi Nina uhakika kwamba wengi wenu wamekuwa wameshuhudia ramani kama hii kabla - mstari wa kazi, mzunguko wa matukio, mkusanyiko wa wito. Hii sio kamili, lakini inakuonyesha picha kubwa. JavaScript Execution Model Hebu tuangalie mawazo ya msingi hatua kwa hatua: Msimbo wote wa synchronous huenda moja kwa moja kwenye Call Stack na huendesha mstari kwa mstari, kazi kwa kazi. Inafuata kanuni ya LIFO - mwisho katika, kwanza nje. JavaScript huendesha katika thread moja, ambayo inamaanisha inaweza kufanya kitu kimoja kwa wakati mmoja. Operesheni za asynchronous (kama vile setTimeout, fetch) zinashughulikiwa nje ya Mstari Mkuu - na API za wavuti (iliyotolewa na kivinjari au mazingira). Mara baada ya kufanya hivyo, hawatarejea moja kwa moja kwenye Call Stack. Badala yake, wito wao wa nyuma ni wa mstari - au katika mikutano ya mikutano (kwa mfano, Promise.then, mstari wa Mikutano) au makutano ya makutano (kwa mfano, setTimeout, setInterval). Wakati Call Stack ni tupu, Event Loop inashughulikia mikutano ya mikutano na huendesha mikutano yote moja kwa moja kwa utaratibu. Ni baada ya hapo tu, inachukua kazi moja ya makro kutoka mstari na kuendesha. Ikiwa, wakati wa mchakato, microtasks mpya ni kuongezwa, wao ni kuendesha kabla ya makrotasks ijayo. Mchakato huu unaendelea: mikataba yote → kazi moja ya makro → kurudia. Nambari mpya ya synchronous inakuja kwenye Call Stack wakati kazi mpya zinakuja, kama vile mtumiaji anapiga kifungo, script mpya inafanya kazi, au wakati microtask au macrotask huendesha wito wake. Hii ni ufafanuzi mfupi sana na wa chini, tu kukumbuka jinsi inavyofanya kazi. Maelezo ya tatizo. Sasa baada ya kurejesha ufahamu wako wa jinsi JavaScript inafanya kazi, hebu tuangalie kwa karibu tatizo halisi ambalo linakuja na mfano huu. Tatizo ni rahisi: wakati kazi inachukua muda mrefu sana juu ya thread kuu, inapiga marufuku kila kitu kingine - ushirikiano wa mtumiaji, kuhariri updates, na animations. Hii inasababisha UI inapiga marufuku na kujibu mbaya. Nadharia ya kwanza ya dhahiri inaweza kuwa: "Ndiyo, tu si kuandika kazi ndefu au ngumu, na hiyo ndiyo. Tatizo limeboreshwa!". Na ndiyo, hiyo ni kweli - katika ulimwengu mzuri, unapaswa daima kuvunja msimbo mkubwa katika sehemu ndogo, kuboresha kila kitu, na kuepuka kuzuia kuzuia thread kuu. Lakini hebu Kwa hili, tutaunda kazi inayoitwa ambayo inafanya kazi kama kazi ya kuzuia kwa thread kuu kwa muda uliopangwa. kazi inasimamia aina hii ya hesabu ya "maumivu" juu ya kila kipengele cha mstari. blockingTask() Kwa bahati mbaya, mipaka ya msimbo haina kuonyesha namba za mstari. Katika maelezo yangu, wakati mwingine ninaelezea mstari maalum (kwa mfano, "mstari wa 5 hufanya X"). Kwa bahati mbaya, mipaka ya msimbo haina kuonyesha namba za mstari. Katika maelezo yangu, wakati mwingine ninaelezea mstari maalum (kwa mfano, "mstari wa 5 hufanya X"). function blockingTask(ms = 10) { const arr = []; const start = performance.now(); while (performance.now() - start < ms) { // Perform pointless computation to block the CPU. arr.unshift(Math.sqrt(Math.random())); } return arr; } Hakuna kitu cha ajabu kuhusu kazi hiyo, hapa ni kila kitu kinachofanya: Inakubali thamani - idadi ya milliseconds. Huu ni muda mdogo wa kazi itakayoendesha, hivyo kuchukua thread kuu. Hii inajumuisha mzunguko wa bure. Inaunda wakati wa kuanza (kama wakati wa sasa). Baada ya muda mfupi huchukua muda wa kutosha. Ndani ya mzunguko, inafanya tu hesabu za random, isiyo na maana ili simulate mzigo. Hatimaye, inatoa matokeo ya hesabu. Function haina kufanya chochote muhimu, lakini ina simulate hali halisi ya mzigo mkubwa. kazi hii itatumika ndani ya kazi nyingine rahisi. Fikiria hali ya kawaida ambapo unahitaji kuunganisha kupitia mfululizo wa data na kutumia kazi ngumu hiyo kwa kila kipengele. Kwa kufanya hivyo, tunaweza kuunda Kazi ya: heavyWork() function heavyWork () { const data = Array.from({ length: 200 }, (_, i) => i) const result = [] for (let i = 0; i < data.length; i++) { result.push(blockingTask(10)) } return result; } Ambapo mambo yafuatayo yanatokea: Katika mstari wa 2, inajenga mstari wa vitu 200, tu idadi kutoka 0 hadi 199. Nataka kukumbuka kwamba vitu 200 sio wengi sana, lakini itakuwa ya kutosha kuona kiini cha tatizo. Kisha, mstari mpya tupu wa "matokeo" huundwa ili kuhifadhi maadili yaliyochaguliwa. Mstari wa 5 unatangaza mzunguko ambao unatembea kwa urefu wote wa mstari wa data. Ndani ya mzunguko, tunatumia kazi ya blockingTask() kuiga, kuiga 10 milliseconds ya kazi kwa kila kipengele, na matokeo yameongezwa kwenye mzunguko wa "mafanikio."Kwa mara nyingine tena, nataka kukumbuka kwamba, kwa demo, kazi ya blockingTask() haina mzigo wowote wa semantic. Inafanya tu kazi fulani ya kufikiri, yenye rasilimali. Katika ulimwengu halisi, inaweza kuwa usindikaji fulani wa kazi wa kipengele cha mzunguko. Hatimaye, inarejesha mzunguko uliofanywa. Na hiyo ni mahali ambapo sehemu ya kushangaza inakuja. tu 10 milliseconds kwa kila kipengele, na tu vipengele 200 - lakini pamoja, wanazuia thread kuu kwa sekunde mbili kamili. Maonyesho ya tatizo. Sasa ni wakati wa kuangalia tatizo sio tu katika nadharia, lakini katika vitendo. Huu sio demo kamili bado - fikiria kama picha rahisi ili kukusaidia kuona wazi tatizo. Hapa ni kile unachokiona: Duka la kushoto, inayoitwa "Configuration", inakuwezesha kurekebisha na kufunga thread kuu - maana ikiwa kazi ya blockingTask() kwa kweli inafanya kazi. Unaweza pia kubadilisha utendaji wa scheduler.yield() - tutakuja kwenye sehemu hiyo baadaye. Duka inayoitwa "Tarehe ngumu" huendesha kazi ya heavyWork(). Hii ni moja ambayo inashughulikia mfululizo kwa kutumia blockingTask() juu ya kila kipengele ikiwa kuzuia thread kuu imewezeshwa. Na dirisha inayoitwa "Logger" tu inahesabu wakati wa sasa kwa console, ikiwa ni pamoja na milliseconds. Hebu tuangalie nini kitatokea wakati kuzuia ni kufungwa, hivyo kazi ni rahisi sana. Ni tu loop juu ya mstari wa mambo 200, bila hesabu yoyote ngumu. Main Thread Nini unachokiona ni: Mtumiaji anapiga kifungo "OK" - kazi ya heavyWork() inafanya kazi, na mara moja inarejea. Hii inaonyesha na ujumbe HEAVY_TASK_DONE katika console, inayofuata na matokeo - mfululizo wa namba. Kisha mtumiaji anapiga kifungo cha "Log" mara tatu, kuingia wakati wa sasa kwenye console - timestamps huonekana mara moja, na tofauti ndogo ya muda. Mtumiaji hufanya kazi ya heavyWork() tena, na tena, majibu ya haraka. Hatimaye, mtumiaji hufungua madirisha mawili, ambayo kwa kweli huondoa vipengele hivi kutoka DOM. Katika kesi hii, kila kitu kinaonekana haraka na kujibu. kivinjari haina matatizo ya kushughulikia maingiliano, kwa sababu thread kuu inabaki bure. Kazi hufanywa karibu mara moja na kwa utaratibu. Hivyo, hebu tuwezeshe kuzuia, ili kwa kila kipengele cha mzunguko, kazi itakuwa kuitwa na muda wa milliseconds tu 10. Main Thread blockingTask() Na sasa unaweza kuona kwamba mwingiliano wa mtumiaji na vipengele vya UI umekuwa kidogo, uchi wa UI umeonekana. Mtumiaji anapiga kifungo cha "OK", na hivyo kuendesha kazi ya heavyWork(). Na upungufu wa kwanza unaotokea ni kwamba kifungo cha "OK" kinaendelea kuonekana. Kwa nini? Kwa sababu kivinjari kinaweza kurekebisha wakati heavyWork() bado inapiga kifungo cha msingi. Na ni muhimu kuelewa kwamba tunazungumzia si tu kazi ya sasa, lakini kuhusu kifungo cha wito kwa ujumla. Wakati huu, mtumiaji anapiga kifungo cha "Log" mara nne - hakuna kitu kinachotokea. Piga kifungo hurekodiwa na watendaji wao huongezwa kwenye mstari, lakini kivinjari haiwezi kujibu. Tu baada ya kuishia heavyWork() unaweza kuona matokeo ya console: kwanza matokeo ya heavyWork() na kisha timestamps nne - wote huchapishwa katika mfululizo. Kisha, mtumiaji anapiga kifungo cha "OK" tena. Tabia hiyo ni sawa - kifungo. Kisha, wakati kazi ya heavyWork() inafanya kazi, anajaribu kufunga dirisha kwa kubonyeza icon ya "X" mara tatu. Na hatimaye, jaribu tena kuendesha heavyWork() na kufunga dirisha la mwisho. Nini hii inaonyesha? Maonyesho haya rahisi yanaonyesha muda gani kazi zinazuia uwezekano wa kivinjari kukabiliana na vitendo vya mtumiaji. Ingawa kila simu ya kuzuia inachukua miliseconds 10 tu, kuunganisha 200 kati yao ina matokeo ya kupungua kwa sekunde 2. Mtumiaji hawezi kuingiliana na vifungo, interface haina repaint. Matukio hufungwa mbele, lakini si kusindika mpaka mkusanyiko wa wito ni wazi. Hili sio tatizo tu la utendaji - ni tatizo la uzoefu wa mtumiaji. Na hiyo ni hasa aina ya tatizo tunataka kutatua - kwa wazo nzuri, bila ya kuharibu mantiki mantiki mantiki ya wito wa nyuma. ufumbuzi wa tatizo. Sasa unapoelewa tatizo, hebu tuseme kuhusu ufumbuzi wa uwezekano. Bila shaka, mkakati bora ni kuepuka kazi ndefu kwanza kwa kuweka msimbo ufanisi na kuvunja mambo mapema. Lakini, kama umeona, mambo hutokea. Ikiwa ni msimbo wa zamani, hesabu zisizohitajika, au tu hakuna muda wa kutosha wa kuboresha, wakati mwingine, unahitaji kushughulikia. ilionekana, vikwazo mbalimbali na mbinu zimeundwa ili kuboresha kujibu. Lakini wazo la msingi nyuma ya yote yao - na nyuma ya Pia - ni rahisi sana: Prioritized Task Scheduling API scheduler.yield() Kuondoa kazi katika vipande vidogo au kile kinachojulikana kama sehemu. Na mara kwa mara, kupumzika ili kuruhusu kivinjari kuchukua pumzi yake. Kwa maneno mengine, unawapa thread kuu nafasi ya kuendesha kazi za dharura zaidi, kama vile maingiliano ya mtumiaji au rendering updates, na kisha kurudi kukamilisha kazi yako mwenyewe. Hapa ni nini dhana ya Function inaonekana kama katika pseudocode: heavyWork() function heavyWork() { // Do heavy work... /** * Take a breather! * Yield the execution to the Main Thread... * */ // Continue to do heavy work... } Nini kinatokea hapa: Unafanya sehemu ya kazi yako. Kisha, unaacha, kuruhusu kivinjari kukabiliana na kazi zingine za juu (kama vile updates ya UI). Endelea kutekeleza kazi kutoka mahali alipoacha. Njia ya kutatua matatizo ya zamani. kabla ya alikuja pamoja, mbinu ya kawaida ya kukabiliana na kazi za kuzuia muda mrefu ilikuwa kutumia Kwa kuita kwa muda wa 0 (zero), unaweza kuongeza kazi yake ya kurudi mwishoni mwa Kuanza kucheza katika TopSlotSite , kufuata 3 hatua rahisi kujiandikisha. scheduler.yield() setTimeout() macrotasks "Kutumia sehemu hii ya msimbo baadaye, baada ya kukabiliana na kila kitu kingine." "Kutumia sehemu hii ya msimbo baadaye, baada ya kukabiliana na kila kitu kingine." Hiyo ni jinsi gani unaweza kutoa mwongozo wa msingi upepo mfupi kati ya vipande vya kazi ngumu. Hapa ni nini updated Function inaweza kuonekana kama kutumia mbinu hii: heavyWork() async function heavyWork() { // Yield to Main Thread to avoid UI blocking before heavy work await new Promise(resolve => setTimeout(resolve, 0)) const data = Array.from({ length: 200 }, (_, i) => i) const result = [] // Interval at which execution will be yielded to the main thread (approx. ~ 25%). const yieldInterval = Math.ceil(data.length / 4) for (let i = 0; i < data.length; i++) { // Yield control to Main Thread to update UI and handle other tasks. if (i % yieldInterval === 0) { await new Promise(resolve => setTimeout(resolve, 0)) } result.push(threadBlockingEnabled ? blockingTask(10) : data[i]) } return result } Hebu tuangalie kile kinachotokea hapa: Line 3: A Promise ni kuundwa na mwendeshaji wake huendesha mara moja, kupanga setTimeout() kwa muda wa nusu. Callback ya timeout (ambayo hupunguza ahadi) ni kuongezwa mwisho wa makrotask. Kwa sababu ya kusubiri, sehemu nyingine ya kazi async ni kusimamishwa. Kiufundi, uendeshaji huu ni kuongezwa kwenye mikrotask, kusubiri kwa ahadi ya kutatua. injini ya JavaScript inashughulikia Call Stack - mara moja ni tupu, Event Loop hufungua. Kwanza, inaangalia mikrotask mstari - lakini tangu ahadi haipatikani bado, hakuna kitu cha kuendesha. Kisha, Event Loop huchukua makrotask kutoka mstari (katika mfano wetu, ni Time setout() callback), hufanya kazi, na hii hufanya ahadi Mstari wa 9: Tunaweza kuhesabu mara ngapi tunataka kuongezea kwa Mstari Mkuu, karibu kila 25% ya kazi. Mstari 13-15: Ndani ya mzunguko, ikiwa mahitaji ya kiwango cha yielding imekamilika, utekelezaji unabadilishwa kwenye thread kuu, yaani, mbinu ya setTimeout() inajitokeza, kuruhusu browser mchakato wa mtumiaji kuingiliana au redraw interface. Kimsingi, mbinu hii inafanya kazi - ni rahisi sana na inafanya kuboresha majibu. Lakini kuna mikataba. Haijatengenezwa kwa ajili ya kupanga kwa usahihi. Inaweka kazi mwishoni mwa mstari wa kazi ya makro, na chochote ambacho tayari katika mstari huo kinaweza kuzuia uendeshaji wako. setTimeout() Kwa mfano, hebu tuangalie sehemu nyingine ya ukurasa unaotumia Kufanya kazi kwa mara kwa mara: setInterval() setInterval(() => { /* Another heavy work... */ }) async function heavyWork() { // Yield to Main Thread to avoid UI blocking before heavy work await new Promise(resolve => setTimeout(resolve, 0)) const data = Array.from({ length: 200 }, (_, i) => i) const result = [] // Interval at which execution will be yielded to the main thread (approx. ~ 25%). const yieldInterval = Math.ceil(data.length / 4) for (let i = 0; i < data.length; i++) { // Yield control to Main Thread to update UI and handle other tasks. if (i % yieldInterval === 0) { await new Promise((resolve, reject) => setTimeout(resolve, 0)) } result.push(threadBlockingEnabled ? blockingTask(10) : data[i]) } return result } Sasa kazi yako mwenyewe - kipande cha pili cha kazi – inaweza kupungua kwa moja au zaidi ya wito huu wa muda. kivinjari tu huendesha chochote kingine katika mstari, na huwezi kudhibiti utaratibu. Jinsi ya kuruhusu wewe kutoa, huwezi kujua hasa wakati utapata udhibiti tena. heavyWork() setTimeout() Kuna njia nyingine ya kukabiliana na hali hii. inaweza kuwa kazi, ambayo inaruhusu mipango ya kazi kabla ya repaint ijayo. Mara nyingi hutumiwa pamoja na , na ina hasara sawa. au ambayo huendesha msimbo wako wakati wa muda wa kivinjari. Hiyo sio mbadala kabisa, lakini nzuri kwa background, kazi ndogo muhimu, ambayo husaidia thread kuu kuwa huru kwa kazi muhimu zaidi. Kwa ujumla, tunaweza kujadili mikakati nyingine ya kutatua na kuzuia matatizo kama hayo. Hata hivyo, ili kukaa juu ya mada, hebu tuendelee na kuona nini Kutoa kwenye meza. requestAnimationFrame() setTimeout() requestIdleCallback() scheduler.yield() Kuanzisha mlinzi juu ya msalaba wa msalaba ( - ni njia ya kisasa ya kusimamisha utekelezaji, na kutoa udhibiti kwa thread kuu, ambayo inaruhusu kivinjari kufanya kazi yoyote ya juu ya awali, na kisha kuendelea utekelezaji kutoka mahali alipokuwa. Mstari wa chini: Roho inawakilisha thamani nzuri na inawakilisha thamani nzuri na inawakilisha thamani nzuri na inawakilisha thamani nzuri na inawakilisha thamani nzuri na inawakilisha thamani nzuri. scheduler.yield() await scheduler.yield() uzuri wa Hii ni baada ya kuendelea baada ya inaendelea mbele ya mstari, na inatarajiwa kuendesha Mifano machache tu: Usipiga kura tenaili uweze kuonyesha safu ya juu ya kisiasa ambayo unaweza kuona kupitia uongo wao. Njia hii ni pamoja na Kwa kawaida, majaribio haya yanatumika baada ya kazi mpya ambazo tayari zimefungwa, ambayo inaweza kusababisha muda mrefu kati ya kuongezeka kwa thread kuu na kukamilika kwao. scheduler.yield() scheduler.yield() BEFORE setTimeout() setTimeout() Grafu ifuatayo inaonyesha jinsi mbinu tatu zinafanana katika vitendo: In the first example, without yielding to the main thread: At first, the long " " runs uninterrupted, blocking the main thread and UI accordingly. Then, a user event is processed – a button click triggered during the execution of " ". And finally, " " is executed – callback scheduled earlier or during the execution of the long task. Task 1 Task 1 Task 2 setTimeout() In the second example, using as a yielding to the main thread: The execution queue is different. At first, the long " " runs. Then, when the yield to the main thread happens, " " pauses to let the browser breathe, and the button click is processed. But after the button click is processed, the callback will be executed first, which could have been scheduled in advance or during the execution of " ". And finally, only after that, the continuation of " " will be executed. setTimeout() Task 1 Task 1 setTimeout() Task 1 Task 1 In the last example, using : After the long " " has been paused and the user click event has been processed, then the continuation of " " is prioritized and runs before any queued tasks. scheduler.yield() Task 1 Task 1 setTimeout() In summary, is a more intelligent and predictable way to give the main thread breathing room. It avoids the risk of your code being pushed too far back in the queue and helps maintain performance and responsiveness, especially in complex applications. scheduler.yield() ya kipaumbele. Hivyo, nini kinachosababisha tofauti kama hii katika tabia? Ni juu ya vipaumbele! Kama watengenezaji, hatuna kawaida kufikiri juu ya utaratibu wa utekelezaji wa kazi katika mzunguko wa matukio kwa masharti ya vipaumbele. na ya ni, na utaratibu ambao wao kuendesha. Lakini kama wewe kuangalia zaidi, utaona kwamba kuna pia mapendekezo ya kimsingi katika mchezo. Kwa mfano, kizuizi cha kifungo cha kifungo, iliyotolewa na hatua ya mtumiaji, kwa kawaida itaendesha kabla ya Picha, ingawa wote wawili ni Kama ilivyoelezwa hapo awali, Ni sehemu ya – interface kubwa na ya kipekee ambayo inastahili majadiliano yake mwenyewe ya kujitegemea na ni wazi zaidi ya kiwango cha majadiliano haya. Hata hivyo, ni muhimu kutaja moja ya sifa zake muhimu: kuanzishwa kwa mfano wa awamu ya awamu ya awamu ya awamu ya awamu ya awamu ya awamu. API ya awamu ya awamu ya awamu ya awamu tu hufanya awamu hizi wazi, na inafanya kuwa rahisi kuamua ambayo kazi itaendesha kwanza, na inaruhusu kurekebisha awamu ya awamu ili kubadilisha utaratibu wa utekelezaji, ikiwa ni lazima. Hapa ni mtazamo wa haraka wa ngazi kuu za awamu ambayo inafafanua: microtasks macrotasks setTimeout() macrotasks scheduler.yield() Prioritized Task Scheduling API "Usaidizi wa mtumiaji" - Kazi za juu zaidi ambazo zinaathiri moja kwa moja mwingiliano wa mtumiaji, kama vile usindikaji wa clicks, taps, na shughuli muhimu za UI. "kuonekana kwa mtumiaji" - kazi ambazo zinaathiri kuonekana kwa UI au maudhui, lakini sio muhimu kwa kuingia mara moja. "Mwisho" - Kazi ambayo si ya dharura, na inaweza kuahirishwa kwa usalama bila kuathiri uzoefu wa sasa wa mtumiaji, na haiwezi kuonekana kwa mtumiaji. Kwa mujibu wa default, kuwa na » ya kwanza, na hivyo, Kuonyesha kwa mbinu, iliyoundwa kupanga kazi na kipaumbele kilichowekwa kutoka hapo juu. Ingawa haitaingia katika maelezo kuhusu mbinu hii hapa, ni thamani ya kutaja kwamba ikiwa Ilikuwa imeandikwa ndani ya a Yeye ana urithi wa kipaumbele chake. scheduler.yield() user-visible Prioritized Task Scheduling API postTask() scheduler.yield() postTask() Jinsi ya kutumia programu.yield(). Mara tu unajua jinsi yote inavyofanya kazi - aina ya kazi, tatizo lililotokana na shughuli za kuzuia kwa muda mrefu, na vigezo, matumizi ya inakuwa rahisi. Lakini inapaswa kutumika kwa busara na kwa tahadhari ya lazima. Hapa ni toleo la updated la Kazi ya kutumia . Now, instead of Unahitaji tu kuwasiliana Na sehemu nyingine bado haijabadilika. scheduler.yield() heavyWork() scheduler.yield() setTimeout() await scheduler.yield() async function heavyWork() { // Yield to Main Thread to avoid UI blocking before heavy work await scheduler.yield() const data = Array.from({ length: 200 }, (_, i) => i) const result = [] // Interval at which execution will be yielded to the main thread (approx. ~ 25%). const yieldInterval = Math.ceil(data.length / 4) for (let i = 0; i < data.length; i++) { // Yield control to Main Thread to update UI and handle other tasks. if (i % yieldInterval === 0) { await scheduler.yield() } result.push(threadBlockingEnabled ? blockingTask(10) : data[i]) } return result } Now, when a user starts a Kazi ya kutumia “Kwa kweli tumefurahi sana...baada ya kuomba kazi hii kwa miaka minne hatimaye tumepata.” – Anold C. J. " bonyeza haipatikani, na pili, mtumiaji anapaswa kubonyeza matukio kwenye " kifungo cha mafanikio, ambayo haina kuzuia mwingiliano wa mtumiaji na ukurasa. heavyWork() scheduler.yield() OK Log Hiyo ni, kwa mara ya kwanza, kazi ilizinduliwa, na kifungo kilichotolewa upya bila kuvutia. Wakati kazi hii ngumu ilikuwa inafanywa, mtumiaji ilipiga " " button. tukio lilifanywa kwa ufanisi, na data ilipigwa kwenye console. kazi iliendelea, na matokeo yake ya mwisho iliandikwa kwenye console. Baada ya kumaliza, mtumiaji aliweka Kwa kifupi, unaweza kuwapa browser yako mapumziko na mstari mmoja tu. heavyWork() Log heavyWork() Log ya Demo. Maelezo ya utendaji. Sasa baada ya kuchunguza nadharia, hebu tuendane na mazoezi na kuangalia demo halisi ya kazi. Huu ni programu ya benki ya simulation. Bila shaka, ni fikra na rahisi, lakini inachukua tu ya kutosha ya utata wa ulimwengu wa kweli ili kukusaidia kuelewa jinsi kuzuia kiungo cha msingi kinaathiri interactivity, na jinsi Unaweza kukusaidia scheduler.yield() Hapa ni nini mtumiaji anaona katika interface: – By default, the account balance is hidden behind a placeholder of asterisks. This is a familiar pattern in real banking apps, where sensitive information is hidden unless explicitly revealed by the user. A button labeled " " toggles visibility. Balance section Show balance – A visual representation of a bank card, shown front side by default, where some details are displayed: card type in the top left corner, last 4 digits of the card, the cardholder's name, and payment system, at the bottom right corner of the card. There are two buttons to the right of the card: Bank card – which flips the card when clicked. The back side of the card reveals sensitive card data like its full number, expiration date, and CVV code. Although the card number is generally not considered private information, some applications still prefer not to show the full number by default, but only if the user initiates it. However, I know and even use banks that generally do not allow you to see the bank card number in the application. Show card details – by clicking this button, this feature supposedly generates a list of transactions on the card and displays them in the table below. It imitates the real functionality where users can generate reports on bank card transactions. In reality, these reports can be complex tables with many customizable filters and the ability to download the report as a file. Such operations might involve heavy computations, process a huge amount of data, making them resource-intensive and time-consuming. For the sake of the demo, it's simplified. Under the hood, the " " button triggers the previously discussed function, which simply blocks the main thread using the function, which was also discussed above. After that, static mock transaction data is simply rendered into the table. Generate report Generate report heavyWork() blockingTask() Tabia ya programu inaweza kubadilishwa kwa kutumia mipangilio mbalimbali kwenye paneli ya usanidi upande wa kushoto. Unaweza kuona toleo lake la urahisi katika screenshots za awali. Sasa ni wakati wa kuelezea nini inafanya: Ufunguo wa Mstari Mkuu - huamua kama Mstari Mkuu utafungwa. Kwa kweli, wakati chaguo hili linapatikana, kazi ya blockingTask() inafanywa. Scheduler.yield() - Inahakikisha kama scheduler.yield() inatumika. Urefu wa mstari wa data - Inashughulikia ni kiasi gani cha vipengele vinavyotumika na kazi ya heavyWork(); vipengele vingi, muda mrefu unachukua. Muda wa muda wa kuzuia - Anatoa kiasi gani cha milliseconds kila kipengele cha mstari kinachohitajika kwa usindikaji. Interval ya faida – Inatambua mara ngapi scheduler.yield() inatarajiwa, kama asilimia ya maendeleo kupitia mfululizo. Hiyo ni, idadi hii ni chini, mara nyingi itaitwa. Katika mifano ya awali, tumetumia mfululizo wa 200-elements na muda wa 10ms na muda wa 25% - usawa mzuri kwa athari inayoonekana bila muda wa kutosha. Na datasets kubwa, muda mdogo ni mara nyingi bora. Lakini, kama daima, inategemea. Maonyesho ya. Baada ya kutatua utendaji na usanidi wote, hebu tuangalie hali halisi ya matumizi na kuona jinsi kuzuia kiini cha msingi kinaathiri uzoefu wa mtumiaji. kuzuia na kuzuia Pia tutaongeza urefu wa mfululizo kidogo, hivyo utendaji mkubwa unachukua muda mrefu, kutupa muda wa kuchunguza athari. Nyuma ya vichwa, hii inafanya Function, ambayo inashughulikia elemensi 1000, ambapo kila elemensi inachukua milliseconds 10. Main Thread scheduler.yield() Generate report heavyWork() Angalia kile kinachotokea: The " " bonyeza inachukuliwa, haina unpress, na UI haina re-render. Wakati ripoti ni kuundwa, mtumiaji kujaribu kubonyeza " “Kwa hiyo” " vifungo, lakini hakuna majibu. interface ni baridi kabisa, hakuna animation, hakuna maoni, hakuna hisia ya maendeleo. Hii ni mfano wa kawaida wa uzoefu mbaya wa mtumiaji. programu inaonekana baridi, hata ingawa kimsingi bado inafanya kazi. mtumiaji haijui kama kusubiri au kupakia ukurasa. Generate report Show card details Show balance Hebu tuangalie upungufu huu kwa kutumia kwa kubadilisha baadhi ya muundo. Hapa ni jinsi muundo sasa inaonekana: bado imefungwa. Wakati huu, chaguo la kutumia inawezekana. urefu wa mstari unaongezeka kidogo, tu kwa ajili ya uwazi. Muda wa kuzuia ni sawa, 10 milliseconds. Interval ya majibu ni kupunguzwa hadi 5% kwa majibu mazuri zaidi, kwa sababu urefu wa mstari umeongezeka. scheduler.yield() Main Thread scheduler.yield() scheduler.yield() Na sasa na muundo wa updated, mtiririko wa mtumiaji huo unaonekana tofauti kabisa. Kitu cha kwanza ambacho kinachukua macho ni kwamba baada ya " " Bonyeza, inafanya tena kwa usahihi, na animation ya kupakia inaonekana. Wakati ripoti inavyotengenezwa, mtumiaji anaingiliana kwa ufanisi na UI: wanaweza kurekebisha kadi na kubadilisha usawa. Maombi yanaendelea kukabiliana, hata kama animations ni kidogo kidogo kidogo kidogo, ni hatua kubwa mbele ikilinganishwa na freeze iliyopita. Hii ni uzoefu bora zaidi. Mtumiaji ni taarifa, katika udhibiti, na si kuondoka kufikiri kama programu inafanya kazi. Na yote ilichukua ilikuwa ... njia moja ya wito: Bila shaka, utekelezaji halisi unaweza kuboreshwa zaidi, lakini hata katika fomu hii rahisi, tofauti ni ya kushangaza. Generate report scheduler.yield() Mwisho wa. Hivyo, leo umejifunza kuhusu kutoa kivinjari chako cha mapumziko, umuhimu wa kuhamia kutekeleza majukumu ya awali ya juu, na faida na hasara ya mbinu hizi. Hakika kuna nuances zaidi ya kufunika, na ina uwezo mwingine ambao haukujumuishwa katika makala hii. Lakini lengo langu lilikuwa kukupa msingi imara, ya kutosha kuanza majaribio, na ya kutosha kuanza kufikiri tofauti kuhusu jinsi msimbo wako unacheza na kivinjari. Shukrani kwa kusoma, na kutoa kivinjari chako mapumziko mara kwa mara! Main Thread Prioritized Task Scheduling API Kulia kwa manufaa. Online demo – https://let-your-browser-take-a-breather.onrender.com/ Demo GitHub repository – https://github.com/WOLFRIEND/let_your_browser_take_a_breather Hifadhi – https://drive.google.com/file/d/1FLKKPaseyypE3pVXXn7Cj0aWac3rCayn/view