Care este cuvântul cheie yield în JavaScript?


Am auzit despre cuvântul cheie „yield” în JavaScript. La ce se folosește și cum îl pot folosi?

Răspuns #1

Adaptarea unui exemplu din „Viitorul JavaScript: Generatoare” de James Long pentru standardul oficial Harmony:

function * foo(x) {
    while (true) {
        x = x * 2;
        yield x;
    }
}

"Când apelezi foo, primești înapoi un obiect Generator care are o metodă next."

var g = foo(2);
g.next(); // -> 4
g.next(); // -> 8
g.next(); // -> 16

Deci yield este oarecum ca return: primești ceva înapoi. return x returnează valoarea lui x, dar yield x returnează o funcție, care îți oferă o metodă de a itera către următoarea valoare. Util dacă ai o procedură care poate consuma multă memorie și pe care ai putea să vrei să o întrerupi în timpul iterației.

Răspunsul #2

Este cu adevărat simplu, așa funcționează :

  • Cuvântul cheie yield ajută pur și simplu la pauză și reluare a unei funcții în orice moment asincron.
  • În plus, ajută să returnați o valoare dintr-o funcție generator.

Ia această funcție simplă de generator:

function* process() {
    console.log('Start process 1');
    console.log('Pause process2 until call next()');

    yield;

    console.log('Resumed process2');
    console.log('Pause process3 until call next()');

    let parms = yield {age: 12};
    console.log("Passed by final process next(90): " + parms);

    console.log('Resumed process3');
    console.log('End of the process function');
}

lasă _process=process();

Până când nu apelezi _process.next(), nu se vor executa primele 2 linii de cod, apoi primul yield va pauza funcția. Pentru a relu funcția până la următorul punct de pauză (cuvânt cheie yield) trebuie să apelezi _process.next().

Poți considera că mai multe yields sunt breakpoints într-un debugger JavaScript într-o singură funcție. Până când nu spui să navigheze la următorul breakpoint, nu va executa blocul de cod. (Notă: fără a bloca întreaga aplicație)

Dar în timp ce yield execută aceste comportamente de pauză și reluare, poate returna și unele rezultate sub forma {value: any, done: boolean} conform funcției anterioare unde nu am emis niciun valori. Dacă explorăm ieșirea anterioară, aceasta va arăta același { value: undefined, done: false } cu valoarea undefined.

Să analizăm cuvântul cheie yield. Opțional, puteți adăuga expressie și să setați o valoare implicită opțională. (Sintaxa documentației oficiale)

[rv] = yield [expression];

expresie: Valoarea de întors din funcția generator

yield any;
yield {age: 12};

rv: Returnează valoarea opțională care a fost transmisă metodei next() a generatorului

Pur și simplu poți transmite parametri funcției process() cu acest mecanism, pentru a executa diferite părți yield.

let val = yield 99; 

_process.next(10);
now the val will be 10 

Încearcă acum

Utilizări :

  • Evaluare leneșă
  • Șiruri infinite
  • Fluxuri de control asincron

Referințe:

  • https://developer.mozilla.org/ro-RO/docs/Web/JavaScript/Reference/Operators/yield
  • http://javascript.tutorialhorizon.com/2015/09/16/generators-and-yield-in-es6/
  • https://strongloop.com/strongblog/how-to-generators-node-js-yield-use-cases/
  • https://medium.com/vptech/o-introducere-practică-la-programarea-constraintelor-2037c91833ba

Răspunsul #3

Documentația MDN este destul de bună, după părerea mea.

Funcția care conține cuvântul cheie yield este un generator. Când o apelezi, parametrii săi formali sunt legați de argumentele efective, dar corpul ei nu este evaluat efectiv. În schimb, se returnează un generator-iterator. Fiecare apel al metodei next() a generator-iteratorului realizează o altă trecere prin algoritmul iterativ. Valoarea fiecărui pas este valoarea specificată de cuvântul cheie yield. Gândește-te la yield ca la versiunea generator-iterator a return, indicând limita fiecărei iterații a algoritmului. De fiecare dată când apelezi next(), codul generatorului reia execuția de la instrucțiunea care urmează după yield.

Răspunsul #4

Simplificând/extinzând răspunsul lui Nick Sotiros (pe care îl consider grozav), cred că este cel mai bine să descriem cum ar începe cineva să programeze folosind yield.

În opinia mea, cel mai mare avantaj al folosirii yield este că va elimina toate problemele cu callback-urile imbricate pe care le vedem în cod. La început e greu de observat acest lucru, motiv pentru care am decis să scriu acest răspuns (pentru mine și, sper, pentru alții!)

Modul în care o face este introducând ideea unui co-rutin, care este o funcție care se poate opri/întrerupe voluntar până obține ceea ce are nevoie. În JavaScript, acest lucru este notat prin function*. Doar funcțiile function* pot folosi yield.

Iată câteva exemple tipice de JavaScript:

loadFromDB('query', function (err, result) {
  // Do something with the result or handle the error
})

Acest lucru este stângaci pentru că acum tot codul tău (care evident trebuie să aștepte acest apel loadFromDB) trebuie să fie în interiorul acestui callback cu aspect urât. Acest lucru este rău din câteva motive...

  • Tot codul tău este indentat cu un nivel mai adânc
  • Ai acest sfârșit }) pe care trebuie să îl urmărești peste tot
  • Toată această aglomerație suplimentară de function (err, result)
  • Nu este foarte clar că faci asta pentru a atribui o valoare lui result

Pe de altă parte, cu yield, toate acestea pot fi realizate într-o singură linie cu ajutorul frumosului cadru de co-rutine.

function* main() {
  var result = yield loadFromDB('query')
}

Și astfel, acum funcția ta principală va ceda unde este necesar atunci când trebuie să aștepte ca variabilele și alte lucruri să se încarce. Dar acum, pentru a rula acest lucru, trebuie să apelezi o funcție normală (funcție non-corutină). Un cadru simplu de corutine poate rezolva această problemă astfel încât tot ce trebuie să faci este să rulezi acest lucru:

start(main())

Și startul este definit (din răspunsul lui Nick Sotiro)

function start(routine, data) {
    result = routine.next(data);
    if(!result.done) {
        result.value(function(err, data) {
            if(err) routine.throw(err); // continue next iteration of routine with an exception
            else start(routine, data);  // continue next iteration of routine normally
        });
    }
}

Și acum, poți avea un cod frumos care este mult mai ușor de citit, ușor de șters și fără a mai fi nevoie să te joci cu indentările, funcțiile etc.

O observație interesantă este că, în acest exemplu, yield este de fapt doar un cuvânt cheie pe care îl poți pune înaintea unei funcții cu un callback.

function* main() {
  console.log(yield function(cb) { cb(null, "Hello World") })
}

Ar afișa „Hello World”. Așadar, poți transforma de fapt orice funcție callback pentru a folosi yield prin simpla creare a aceleiași semnături de funcție (fără cb) și returnând function (cb) {}, astfel:

function yieldAsyncFunc(arg1, arg2) {
  return function (cb) {
    realAsyncFunc(arg1, arg2, cb)
  }
}

Sperăm ca, cu aceste cunoștințe, să poți scrie un cod mai curat, mai ușor de citit și ușor de șters!

Răspunsul #5

Cuvântul cheie Yield într-o funcție JavaScript o face generator,

Ce este un generator în JavaScript?

Un generator este o funcție care produce o secvență de rezultate în loc de o singură valoare, adică generezi o serie de valori

Generatorii de sens ne ajută să lucrăm asincron cu ajutorul iteratorilor. Oh, acum ce naiba sunt iteratorii? Chiar?

Iteratorii sunt mijloace prin care putem accesa elementele câte unul pe rând

De unde ne ajută iteratorul să accesăm elementele unul câte unul? Ne ajută să accesăm elementele prin funcții generator, funcțiile generator sunt acelea în care folosim cuvântul cheie yield, cuvântul cheie yield ne ajută să întrerupem și să reluăm execuția funcției.

Iată un exemplu rapid:

function *getMeDrink() {

    let question1 = yield 'soda or beer'; // execution will pause here because of yield
       
    if (question1 == 'soda') {
        return 'here you get your soda';
    }

    if (question1 == 'beer') {

        let question2 = yield 'What\'s your age'; // execution will pause here because of yield

        if (question2 > 18) {
            return "ok you are eligible for it";
        } else {
            return "Shhhh!!!!";
        }
    }
}

let _getMeDrink = getMeDrink(); // initialize it

_getMeDrink.next().value; // "soda or beer"

_getMeDrink.next('beer').value; // "What's your age"

_getMeDrink.next('20').value; // "ok you are eligible for it"

_getMeDrink.next().value; // undefined

Permiteți-mi să explic pe scurt ce se întâmplă

Ați observat că execuția este pusă în pauză la fiecare cuvânt cheie yield și putem accesa primul yield cu ajutorul iteratorului .next()

Aceasta iterează prin toate cuvintele cheie yield unul câte unul și apoi returnează undefined atunci când nu mai rămân cuvinte cheie yield; pe scurt, poți spune că cuvântul cheie yield este un punct de oprire unde funcția se întrerupe de fiecare dată și se reia doar când este apelată folosind iteratorul; în cazul nostru: _getMeDrink.next(); acesta este un exemplu de iterator care ne ajută să accesăm fiecare punct de oprire din funcție.

Exemplu de Generatoare: async/await

Dacă vezi implementarea lui async/await, vei observa că sunt folosite funcții generator & promisiuni pentru a face să funcționeze async/await. Te rog să indici orice sugestii, toate sunt binevenite.

Răspuns #6

Pentru a oferi un răspuns complet: yield funcționează similar cu return, dar într-un generator.

În ceea ce privește exemplul dat frecvent, acesta funcționează după cum urmează:

function *squareGen(x) {
    var i;
    for (i = 0; i < x; i++) {
        yield i*i;
    }
}

var gen = squareGen(3);

console.log(gen.next().value); // prints 0
console.log(gen.next().value); // prints 1
console.log(gen.next().value); // prints 4

Dar există și un al doilea scop al cuvântului cheie yield. Poate fi folosit pentru a trimite valori către generator.

Pentru a clarifica, un mic exemplu:

function *sendStuff() {
    y = yield (0);
    yield y*y;
}

var gen = sendStuff();

console.log(gen.next().value); // prints 0
console.log(gen.next(2).value); // prints 4

Acesta funcționează, deoarece valoarea 2 este atribuită lui y, trimițând-o generatorului, după ce acesta s-a oprit la primul yield (care a returnat 0).

Acest lucru ne permite să facem niște lucruri cu adevărat ciudate. (caută coroutine)

Răspunsul #7

Este folosit pentru iteratori-generatori. Practic, îți permite să creezi o secvență (potențial infinită) folosind cod procedural. Vezi documentația Mozilla.