Słyszałem o słowie kluczowym „wydajność” w JavaScript, ale znalazłem bardzo słabą dokumentację na jego temat. Czy ktoś może mi wyjaśnić (lub polecić witrynę, która wyjaśnia) jego użycie i do czego służy?
Słyszałem o słowie kluczowym „wydajność” w JavaScript, ale znalazłem bardzo słabą dokumentację na jego temat. Czy ktoś może mi wyjaśnić (lub polecić witrynę, która wyjaśnia) jego użycie i do czego służy?
Odpowiedzi:
Dokumentacja MDN jest całkiem dobra, IMO.
Funkcja zawierająca słowo kluczowe wydajności jest generatorem. Kiedy go wywołujesz, jego parametry formalne są powiązane z faktycznymi argumentami, ale jego treść nie jest w rzeczywistości oceniana. Zamiast tego zwracany jest iterator generatora. Każde wywołanie metody next () generatora-iteratora wykonuje kolejne przejście przez algorytm iteracyjny. Wartość każdego kroku jest wartością określoną przez słowo kluczowe fed. Pomyśl o wydajności jako o wersji generatora i iteratora, wskazującej granicę między każdą iteracją algorytmu. Za każdym razem, gdy wywołujesz next (), kod generatora jest wznawiany z instrukcji następującej po wydajności.
Późne odpowiedzi, prawdopodobnie wszyscy wiedzą o tym yield
teraz, ale pojawiła się lepsza dokumentacja.
Na podstawie przykładu Jamesa Longa z „Javascript's Future: Generators” dla oficjalnego standardu Harmony:
function * foo(x) {
while (true) {
x = x * 2;
yield x;
}
}
„Kiedy wywołujesz foo, odzyskujesz obiekt Generator, który ma następną metodę.”
var g = foo(2);
g.next(); // -> 4
g.next(); // -> 8
g.next(); // -> 16
To yield
coś w rodzaju return
: dostajesz coś z powrotem. return x
zwraca wartość x
, ale yield x
zwraca funkcję, która daje metodę iteracji w kierunku następnej wartości. Przydatne, jeśli masz procedurę potencjalnie obciążającą pamięć, którą możesz chcieć przerwać podczas iteracji.
function* foo(x){
tam jest
*
token . To, czy jest to potrzebne, zależy od tego, jaką przyszłość powrócisz. Szczegół jest długi: GvR wyjaśnia to dla implementacji Pythona , na której modelowana jest implementacja JavaScript. Używanie function *
zawsze będzie właściwe, choć w niektórych przypadkach jest nieco większe niż w function
przypadku yield
.
function *
i yield
, i dodała cytowany błąd („Wczesny błąd powstaje, jeśli wydajność lub wyrażenie * występuje w funkcji innej niż generator”). Ale oryginalna implementacja Javascript 1.7 w Firefoksie nie wymagała*
. Zaktualizowałem odpowiednio odpowiedź. Dzięki!
To jest naprawdę proste, tak to działa
yield
Słowo kluczowe pomaga po prostu wstrzymać i wznowić funkcję w dowolnym momencie asynchronicznie .Weź tę prostą funkcję generatora :
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');
}
niech _process = process ();
Do czasu wywołania _process.next () to przyzwyczajenie wykonywania przez pierwsze 2 linie kodu, to pierwszy plon będzie wstrzymać funkcję. Aby wznowić funkcję do następnego punktu pauzy ( słowo kluczowe wydajności ), musisz wywołać _process.next () .
Można pomyśleć, że wiele wydajności to punkty przerwania w debuggerze javascript w ramach jednej funkcji. Dopóki nie powiesz, aby przejść do następnego punktu przerwania, nie wykona bloku kodu. ( Uwaga : bez blokowania całej aplikacji)
Ale jednocześnie wykonuje yield to wstrzymać i wznowić zachowań może powrócić pewne rezultaty , jak również {value: any, done: boolean}
zgodnie z poprzedniej funkcji my nie emitują żadnych wartości. Jeśli zbadamy poprzednie wyjście, pokaże to samo { value: undefined, done: false }
z niezdefiniowaną wartością .
Pozwala zagłębić się w słowo kluczowe wydajności. Opcjonalnie możesz dodać wyrażenie i ustawić przypisanie domyślnej wartości opcjonalnej . (Oficjalna składnia dokumentu)
[rv] = yield [expression];
wyrażenie : wartość do zwrócenia z funkcji generatora
yield any;
yield {age: 12};
rv : Zwraca opcjonalną wartość, która została przekazana do metody next () generatora
Po prostu możesz przekazać parametry do funkcji process () za pomocą tego mechanizmu, aby wykonać różne części wydajności.
let val = yield 99;
_process.next(10);
now the val will be 10
Zastosowania
Bibliografia:
Upraszczając / opracowując odpowiedź Nicka Sotirosa (co moim zdaniem jest niesamowite), myślę, że najlepiej opisać, jak zacząć kodować yield
.
Moim zdaniem największą zaletą używania yield
jest to, że wyeliminuje wszystkie zagnieżdżone problemy zwrotne, które widzimy w kodzie. Trudno na początku zrozumieć, dlatego postanowiłem napisać tę odpowiedź (dla siebie i mam nadzieję, że inni!)
Robi to, wprowadzając ideę rutyny, która jest funkcją, która może dobrowolnie zatrzymać / wstrzymać, dopóki nie osiągnie tego, czego potrzebuje. W javascript jest to oznaczone przez function*
. function*
Można używać tylko funkcji yield
.
Oto typowy javascript:
loadFromDB('query', function (err, result) {
// Do something with the result or handle the error
})
Jest to niezgrabne, ponieważ teraz cały kod (który oczywiście musi czekać na to loadFromDB
wywołanie) musi znajdować się w tym brzydko wyglądającym wywołaniu zwrotnym. Jest to złe z kilku powodów ...
})
którego musisz wszędzie śledzićfunction (err, result)
żargonresult
Z drugiej strony, yield
wszystko to można zrobić w jednym wierszu za pomocą ładnego wspólnego schematu.
function* main() {
var result = yield loadFromDB('query')
}
I tak teraz twoja główna funkcja przyniesie w razie potrzeby, gdy będzie musiała czekać na załadowanie zmiennych i rzeczy. Ale teraz, aby to uruchomić, musisz wywołać normalną (nieskorupcyjną funkcję). Prosta wspólna struktura może rozwiązać ten problem, więc wystarczy, że uruchomisz:
start(main())
I początek jest zdefiniowany (z odpowiedzi Nicka 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
});
}
}
A teraz możesz mieć piękny kod, który jest o wiele bardziej czytelny, łatwy do usunięcia i nie musisz majstrować przy wcięciach, funkcjach itp.
Ciekawym spostrzeżeniem jest to, że w tym przykładzie yield
jest to po prostu słowo kluczowe, które można umieścić przed funkcją z wywołaniem zwrotnym.
function* main() {
console.log(yield function(cb) { cb(null, "Hello World") })
}
Wydrukowałby „Hello World”. Więc możesz faktycznie zmienić dowolną funkcję zwrotną w użycie yield
, po prostu tworząc tę samą sygnaturę funkcji (bez cb) i zwracając function (cb) {}
, tak jak poniżej:
function yieldAsyncFunc(arg1, arg2) {
return function (cb) {
realAsyncFunc(arg1, arg2, cb)
}
}
Mam nadzieję, że dzięki tej wiedzy możesz napisać czystszy, bardziej czytelny kod, który można łatwo usunąć !
function*
jest zwykłą funkcją bez wydajności?
function *
jest to funkcja, która zawiera wydajność. Jest to specjalna funkcja zwana generatorem.
yield
wszędzie, jestem pewien, że ma to większy sens niż wywołania zwrotne, ale nie rozumiem, w jaki sposób jest to bardziej czytelne niż wywołania zwrotne.
Aby dać pełną odpowiedź: yield
działa podobnie return
, ale w generatorze.
W przypadku często podawanego przykładu działa to w następujący sposób:
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
Ale jest też drugi cel słowa kluczowego wydajności. Można go użyć do przesłania wartości do generatora.
Aby wyjaśnić, mały przykład:
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
Działa to, jak 2
przypisuje się wartość y
, wysyłając ją do generatora po zatrzymaniu z pierwszą wydajnością (która zwróciła 0
).
To pozwala nam na naprawdę fajne rzeczy. (spójrz na coroutine)
Służy do generowania iteratorów. Zasadniczo pozwala na utworzenie (potencjalnie nieskończonej) sekwencji przy użyciu kodu proceduralnego. Zobacz dokumentację Mozilli .
yield
może być również użyty do wyeliminowania piekła wywołania zwrotnego, dzięki ramowej strukturze.
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
});
}
}
// with nodejs as 'node --harmony'
fs = require('fs');
function read(path) {
return function(callback) { fs.readFile(path, {encoding:'utf8'}, callback); };
}
function* routine() {
text = yield read('/path/to/some/file.txt');
console.log(text);
}
// with mdn javascript 1.7
http.get = function(url) {
return function(callback) {
// make xhr request object,
// use callback(null, resonseText) on status 200,
// or callback(responseText) on status 500
};
};
function* routine() {
text = yield http.get('/path/to/some/file.txt');
console.log(text);
}
// invoked as.., on both mdn and nodejs
start(routine());
Generator sekwencji Fibonacciego wykorzystujący słowo kluczowe fed.
function* fibbonaci(){
var a = -1, b = 1, c;
while(1){
c = a + b;
a = b;
b = c;
yield c;
}
}
var fibonacciGenerator = fibbonaci();
fibonacciGenerator.next().value; // 0
fibonacciGenerator.next().value; // 1
fibonacciGenerator.next().value; // 1
fibonacciGenerator.next().value; // 2
Yeild
słowo kluczowe w funkcji javaScript powoduje, że jest generatorem,
co to jest generator w javaScript?
Generator to funkcja, która generuje sekwencję wyników zamiast pojedynczej wartości, tj. Generujesz serię wartości
Generatory znaczeń pomagają nam pracować asynchronicznie z iteratorami pomocy. Och, czym są iteratory hacka? naprawdę?
Iteratory to środki, dzięki którym możemy uzyskać dostęp do przedmiotów pojedynczo
skąd iterator pomaga nam uzyskiwać dostęp do elementu pojedynczo? pomaga nam uzyskać dostęp do przedmiotów poprzez funkcje generatora,
funkcje generatora to te, w których używamy yeild
słowa kluczowego, słowo kluczowe wydajności pomaga nam w wstrzymywaniu i wznawianiu wykonywania funkcji
Oto szybki przykład
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 'Whats 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 // "Whats your age"
_getMeDrink.next('20').value // "ok you are eligible for it"
_getMeDrink.next().value // undefined
pozwól mi wyjaśnić, co się dzieje
zauważyłeś, że wykonanie jest wstrzymywane przy każdym yeild
słowie kluczowym, a my możemy uzyskać do niego dostęp yield
za pomocą iteratora.next()
powoduje to powtarzanie wszystkich yield
słów kluczowych pojedynczo, a następnie zwraca niezdefiniowane, gdy nie ma już yield
słów kluczowych w prostych słowach, które można wypowiedziećyield
słowo kluczowe jest punktem przerwania, w którym funkcja za każdym razem zatrzymuje się i wznawia tylko po wywołaniu za pomocą iteratora
w naszym przypadku: _getMeDrink.next()
jest to przykład iteratora, który pomaga nam uzyskać dostęp do każdego punktu przerwania w funkcji
Przykład generatorów:
async/await
jeśli zobaczysz wdrożenie async/await
, zobaczysz, że generator functions & promises
są używane do async/await
pracy
proszę zauważyć, że wszelkie sugestie są mile widziane
Zależność między asynchronicznymi wywołaniami javascript.
Kolejny dobry przykład wykorzystania plonu.
function request(url) {
axios.get(url).then((reponse) => {
it.next(response);
})
}
function* main() {
const result1 = yield request('http://some.api.com' );
const result2 = yield request('http://some.otherapi?id=' + result1.id );
console.log('Your response is: ' + result2.value);
}
var it = main();
it.next()
Zanim dowiesz się o wydajności, musisz wiedzieć o generatorach. Generatory są tworzone przy użyciu function*
składni. Funkcje generatora nie wykonują kodu, ale zwracają typ iteratora zwany generatorem. Gdy wartość jest podana za pomocą next
metody, funkcja generatora działa, dopóki nie natrafi na słowo kluczowe wydajności. Użycie yield
zwraca obiekt zawierający dwie wartości, jedna jest wartością, a druga jest zakończona (boolean). Wartością może być tablica, obiekt itp.
Prosty przykład:
const strArr = ["red", "green", "blue", "black"];
const strGen = function*() {
for(let str of strArr) {
yield str;
}
};
let gen = strGen();
for (let i = 0; i < 5; i++) {
console.log(gen.next())
}
//prints: {value: "red", done: false} -> 5 times with different colors, if you try it again as below:
console.log(gen.next());
//prints: {value: undefined, done: true}
Próbuję również zrozumieć słowo kluczowe wydajności. W oparciu o moje obecne rozumienie, w generatorze słowo kluczowe wydajności działa jak przełącznik kontekstowy procesora. Po uruchomieniu instrukcji dochodu wszystkie stany (na przykład zmienne lokalne) są zapisywane.
Poza tym obiekt wywołujący zostanie zwrócony bezpośrednio do obiektu wywołującego, na przykład {wartość: 0, done: false}. Program wywołujący może użyć tego obiektu wynikowego, aby zdecydować, czy „obudzić” generator ponownie, wywołując next () (wywołanie next () ma na celu iterację wykonania).
Inną ważną rzeczą jest to, że może ustawić wartość zmiennej lokalnej. Tę wartość może przekazać osoba wywołująca „next ()” podczas „budzenia” generatora. na przykład it.next ('valueToPass'), na przykład: „resultValue = return slowQuery (1);” Podobnie jak w przypadku budzenia następnego wykonania, program wywołujący może wstrzyknąć do wykonania jakiś wynik (wstrzyknąć go do zmiennej lokalnej). Zatem dla tego wykonania istnieją dwa rodzaje stanów:
kontekst zapisany w ostatnim wykonaniu.
Wprowadzane wartości przez wyzwalacz tego wykonania.
Dzięki tej funkcji generator może uporządkować wiele operacji asynchronicznych. Wynik pierwszego zapytania asynchronicznego zostanie przekazany do drugiego poprzez ustawienie zmiennej lokalnej (resultValue w powyższym przykładzie). Drugie zapytanie asynchroniczne może zostać wyzwolone tylko przez odpowiedź pierwszego zapytania asynchronicznego. Następnie drugie zapytanie asynchroniczne może sprawdzić wartość zmiennej lokalnej, aby zdecydować o kolejnych krokach, ponieważ zmienna lokalna jest wartością wstrzykniętą z odpowiedzi na pierwsze zapytanie.
Trudności związane z zapytaniami asynchronicznymi są następujące:
piekło zwrotne
tracą kontekst, chyba że przekażą je jako parametry w wywołaniu zwrotnym.
Wydajność i generator mogą pomóc w obu przypadkach.
Bez wydajności i generatora uporządkowanie wielu zapytań asynchronicznych wymaga zagnieżdżonego wywołania zwrotnego z parametrami jako kontekstem, co nie jest łatwe do odczytania i utrzymania.
Poniżej znajduje się przykładowy łańcuch zapytań asynchronicznych działający z nodejs:
const axios = require('axios');
function slowQuery(url) {
axios.get(url)
.then(function (response) {
it.next(1);
})
.catch(function (error) {
it.next(0);
})
}
function* myGen(i=0) {
let queryResult = 0;
console.log("query1", queryResult);
queryResult = yield slowQuery('https://google.com');
if(queryResult == 1) {
console.log("query2", queryResult);
//change it to the correct url and run again.
queryResult = yield slowQuery('https://1111111111google.com');
}
if(queryResult == 1) {
console.log("query3", queryResult);
queryResult = yield slowQuery('https://google.com');
} else {
console.log("query4", queryResult);
queryResult = yield slowQuery('https://google.com');
}
}
console.log("+++++++++++start+++++++++++");
let it = myGen();
let result = it.next();
console.log("+++++++++++end+++++++++++");
Poniżej znajduje się bieżący wynik:
+++++++++++ start +++++++++++
zapytanie1 0
+++++++++++ end +++++++++++
zapytanie2 1
zapytanie4 0
Poniższy wzorzec stanu może zrobić podobnie dla powyższego przykładu:
const axios = require('axios');
function slowQuery(url) {
axios.get(url)
.then(function (response) {
sm.next(1);
})
.catch(function (error) {
sm.next(0);
})
}
class StateMachine {
constructor () {
this.handler = handlerA;
this.next = (result = 1) => this.handler(this, result);
}
}
const handlerA = (sm, result) => {
const queryResult = result; //similar with generator injection
console.log("query1", queryResult);
slowQuery('https://google.com');
sm.handler = handlerB; //similar with yield;
};
const handlerB = (sm, result) => {
const queryResult = result; //similar with generator injection
if(queryResult == 1) {
console.log("query2", queryResult);
slowQuery('https://1111111111google.com');
}
sm.handler = handlerC; //similar with yield;
};
const handlerC = (sm, result) => {
const queryResult = result; //similar with generator injection;
if (result == 1 ) {
console.log("query3", queryResult);
slowQuery('https://google.com');
} else {
console.log("query4", queryResult);
slowQuery('https://google.com');
}
sm.handler = handlerEnd; //similar with yield;
};
const handlerEnd = (sm, result) => {};
console.log("+++++++++++start+++++++++++");
const sm = new StateMachine();
sm.next();
console.log("+++++++++++end+++++++++++");
Oto wynik działania:
+++++++++++ start +++++++++++
zapytanie1 0
+++++++++++ end +++++++++++
zapytanie2 1
zapytanie4 0
nie zapomnij o bardzo pomocnej składni „x generatora”, aby przejść przez generator. W ogóle nie trzeba używać funkcji next ().
function* square(x){
for(i=0;i<100;i++){
x = x * 2;
yield x;
}
}
var gen = square(2);
for(x of gen){
console.log(x);
}