JavaScript'te bir "verim" anahtar kelimesi duydum, ancak bununla ilgili çok kötü belgeler buldum. Birisi bana kullanımını ve ne için kullanıldığını açıklayabilir (veya açıklayan bir site önerebilir)?
JavaScript'te bir "verim" anahtar kelimesi duydum, ancak bununla ilgili çok kötü belgeler buldum. Birisi bana kullanımını ve ne için kullanıldığını açıklayabilir (veya açıklayan bir site önerebilir)?
Yanıtlar:
MDN dokümanları IMO, oldukça iyidir.
Ürün anahtar kelimesini içeren işlev bir oluşturucudur. Bunu çağırdığınızda, resmi parametreleri gerçek bağımsız değişkenlere bağlıdır, ancak gövdesi aslında değerlendirilmez. Bunun yerine, bir jeneratör-yineleyici döndürülür. Generator-iterator'ın next () yöntemine yapılan her çağrı, yinelemeli algoritmadan başka bir geçiş gerçekleştirir. Her adımın değeri, verim anahtar kelimesi tarafından belirtilen değerdir. Verimi, algoritmanın her yinelemesi arasındaki sınırı belirten, geri dönüşün jeneratör-yineleyici versiyonu olarak düşünün. Next () öğesini her aradığınızda, jeneratör kodu verimi izleyen ifadeden devam eder.
Geç cevaplama, muhtemelen herkes yield
şimdi biliyor , ancak daha iyi belgeler geldi.
James Harmony'nin resmi Harmony standardı için "Javascript'in Geleceği: Jeneratörler" in bir örneğini uyarlamak :
function * foo(x) {
while (true) {
x = x * 2;
yield x;
}
}
"Foo'yu çağırdığınızda, bir sonraki yönteme sahip bir Generator nesnesini geri alırsınız."
var g = foo(2);
g.next(); // -> 4
g.next(); // -> 8
g.next(); // -> 16
Yani yield
nazik benzeri olan return
: bir şey kolla. return x
değerini döndürür x
, ancak yield x
bir sonraki değere doğru yineleme yöntemi sağlayan bir işlev döndürür. Faydalı bir varsa potansiyel bellek yoğun prosedürü size yineleme sırasında kesmek isteyebilirsiniz.
function* foo(x){
orada
*
belirteç eklemek için genişletilir . İhtiyacınız olup olmadığı iade ettiğiniz geleceğe bağlıdır. Ayrıntı uzun: GvR bunu Javascript uygulamasının modellentiği Python uygulaması için açıklıyor . Kullanımı function *
her zaman biraz daha masraf daha bazı durumlarda da, doğru olacaktır function
ile yield
.
function *
ve yield
belirtilen hatayı ekledi ("Jeneratör olmayan bir fonksiyonda bir verim veya verim * ifadesi meydana gelirse erken bir hata ortaya çıkar"). Ama, Firefox'ta orijinal JavaScript 1.7 uygulaması gerektirmeyen*
. Cevap buna göre güncellendi. Teşekkürler!
Gerçekten Basit, Nasıl Çalışır
yield
anahtar kelime, bir işlevi zaman uyumsuz olarak duraklatmaya ve devam ettirmeye yardımcı olur .Bu basit jeneratör işlevini kullanın:
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');
}
let _process = process ();
Aradığınızda kadar () _process.next o alışkanlık yürütmek ilk 2 çizgiler kodun ardından ilk verim olacaktır pause fonksiyonu. Bir sonraki duraklama noktasına ( verim anahtar kelimesi ) kadar işlevi sürdürmek için _process.next () öğesini çağırmanız gerekir .
Birden çok verimin , tek bir işlev içindeki bir javascript hata ayıklayıcısındaki kesme noktaları olduğunu düşünebilirsiniz . Bir sonraki kesme noktasında gezinmeyi söyleyene kadar kod bloğunu yürütmez. ( Not : tüm uygulamayı engellemeden)
Ama verim gerçekleştirdiği bu duraklatma ve devam etme davranışları ederken yapabilirsiniz bazı sonuç sıra {value: any, done: boolean}
biz tüm değerleri yayarlar değil önceki fonksiyonuna göre. Önceki çıktıyı araştırırsak, tanımsız{ value: undefined, done: false }
değerle aynı şeyi gösterir .
Ürün anahtar kelimesine girelim. İsteğe bağlı olarak ifade ekleyebilir ve varsayılan bir isteğe bağlı değer atayabilirsiniz . (Resmi doküman sözdizimi)
[rv] = yield [expression];
expression : Generator fonksiyonundan döndürülecek değer
yield any;
yield {age: 12};
rv : Üretecin next () yöntemine iletilen isteğe bağlı değeri döndürür
Farklı verim parçalarını yürütmek için parametreleri bu mekanizma ile process () işlevine iletebilirsiniz.
let val = yield 99;
_process.next(10);
now the val will be 10
Usages
Referanslar:
Nick Sotiros'un cevabını basitleştiren / ayrıntılandıran (ki bu harika olduğunu düşünüyorum), birinin kodlamaya nasıl başlayacağını tanımlamanın en iyisi olduğunu düşünüyorum yield
.
Kanımca, kullanmanın en büyük avantajı, yield
kodda gördüğümüz tüm iç içe geri arama sorunlarını ortadan kaldırmasıdır. İlk başta nasıl olduğunu görmek zor, bu yüzden bu cevabı yazmaya karar verdim (kendim ve umarım diğerleri için!)
Yaptığı yol, ihtiyaç duyduğu şeyi alana kadar gönüllü olarak durdurabilen / duraklatabilen bir işlev olan bir rutin fikrini tanıtmaktır. Javascript'te, bu ile gösterilir function*
. Yalnızca function*
fonksiyonlar kullanılabilir yield
.
İşte bazı tipik javascriptler:
loadFromDB('query', function (err, result) {
// Do something with the result or handle the error
})
Bu hantal çünkü şimdi kodunuzun (ki bu loadFromDB
çağrıyı beklemesi gerekiyor) bu çirkin görünümlü geri arama içinde olması gerekiyor. Bu birkaç nedenden dolayı kötü ...
})
yerde takip etmeniz gereken bu sonunuz varfunction (err, result)
jargonresult
Öte yandan, yield
tüm bunlar hoş bir rutin çerçevenin yardımıyla tek bir satırda yapılabilir .
function* main() {
var result = yield loadFromDB('query')
}
Ve şimdi ana fonksiyonunuz, değişkenlerin ve şeylerin yüklenmesini beklemesi gerektiğinde, gerektiğinde verilecektir. Ama şimdi, bunu çalıştırmak için normal bir (koroutin olmayan fonksiyon) çağırmanız gerekir. Basit bir rutin çerçeve bu sorunu düzeltebilir, böylece tek yapmanız gereken bunu çalıştırmaktır:
start(main())
Ve başlangıç tanımlandı (Nick Sotiro'nun cevabından)
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
});
}
}
Ve şimdi, çok daha okunabilir, silinmesi kolay ve girintiler, işlevler vb. İle uğraşmanıza gerek olmayan güzel kodlara sahip olabilirsiniz.
İlginç bir gözlem, bu örnekte, yield
aslında bir geri arama işlevinden önce koyabileceğiniz bir anahtar kelime olmasıdır.
function* main() {
console.log(yield function(cb) { cb(null, "Hello World") })
}
"Merhaba Dünya" yazdırır. Böylece yield
, sadece aynı işlev imzasını (cb olmadan) oluşturarak ve geri dönerek function (cb) {}
, herhangi bir geri çağırma işlevini kullanıma dönüştürebilirsiniz :
function yieldAsyncFunc(arg1, arg2) {
return function (cb) {
realAsyncFunc(arg1, arg2, cb)
}
}
Umarım bu bilgi ile silinmesi kolay daha temiz, daha okunabilir bir kod yazabilirsiniz !
function*
sadece verimsiz düzenli bir işlev midir?
function *
bu verim içeren bir işlev . Jeneratör adı verilen özel bir işlev.
yield
her yerde kullanan insanlar için , bunun geri aramalardan daha mantıklı olduğuna eminim, ancak bunun geri aramalardan nasıl daha okunabilir olduğunu göremiyorum.
Tam bir cevap vermek için: yield
benzer return
, ancak bir jeneratörde çalışıyor.
Yaygın olarak verilen örneğe gelince, bu şu şekilde çalışır:
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
Ancak, anahtar kelime veriminin ikinci bir amacı da vardır. Jeneratöre değer göndermek için kullanılabilir.
Açıklığa kavuşturmak için küçük bir örnek:
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
Bu, değer 2
atandığı gibi y
, ilk verimde (geri dönen 0
) durduktan sonra jeneratöre gönderilerek çalışır .
Bu bize gerçekten korkak şeyler yapmamızı sağlıyor. (programa bak)
Yineleyiciler için kullanılır. Temel olarak, prosedür kodunu kullanarak (potansiyel olarak sonsuz) bir sekans oluşturmanıza izin verir. Mozilla'nın belgelerine bakın .
yield
Coroutine çerçeveli geri arama cehennemini ortadan kaldırmak için de kullanılabilir.
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());
Ürün anahtar kelimesini kullanarak Fibonacci dizi üreteci.
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
javaScript işlevindeki anahtar kelime, onu jeneratör yapar,
javaScript'te jeneratör nedir?
Jeneratör, tek bir değer yerine bir sonuç dizisi üreten bir işlevdir, yani bir dizi değer üretirsiniz
Anlam üreteçleri yardım yineleyicilerle eşzamansız çalışmamıza yardımcı olur, şimdi hack yineleyiciler nelerdir? Gerçekten mi?
Yineleyiciler, öğelere birer birer erişebildiğimiz anlamına gelir
yineleyici öğeye birer birer erişmemize nereden yardım eder? jeneratör fonksiyonlarıyla öğelere erişmemize yardımcı olur,
jeneratör fonksiyonları kullandığımız işlevlerdir yeild
anahtar kelime anahtar kelime, işlevin yürütülmesini duraklatmaya ve devam ettirmemize yardımcı olur
işte hızlı örnek
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
neler olduğunu kısaca açıklayayım
her birinde yürütmenin duraklatıldığını fark ettiniz yeild
anahtar kelimede ve ilk önce yield
yineleyici yardımıyla erişebiliyoruz.next()
bu, tüm yield
anahtar kelimeleri teker teker yineler ve daha sonra artık tanımsızsa döndürüryield
basit kelimelerle söylenebilecek anahtar kelime kalmadığındayield
kelimenin her seferinde işlev durakladığı ve yalnızca yineleyici kullanarak aradığınızda devam ettiği kesme noktası
bizim durumumuz için: _getMeDrink.next()
bu, işlevdeki her kırılma noktasına erişmemize yardımcı olan yineleyicinin bir örneğidir
Jeneratör Örneği:
async/await
Eğer uygulamanızın yapıldığını async/await
göreceksinizgenerator functions & promises
async/await
iş
herhangi bir öneri memnuniyetle işaret lütfen
Zaman uyumsuz javascript çağrıları arasındaki bağımlılık.
Verimin nasıl kullanılabileceğine bir başka iyi örnek.
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()
Verimi öğrenmeden önce jeneratörler hakkında bilmeniz gerekir. Jeneratörler function*
sözdizimi kullanılarak oluşturulur . Jeneratör işlevleri kod yürütmez, bunun yerine jeneratör adı verilen bir yineleyici türü döndürür. next
Yöntem kullanılarak bir değer verildiğinde , jeneratör işlevi bir verim anahtar sözcüğüyle karşılaşana kadar çalışmaya devam eder. Kullanarak yield
biri iki değer içeren bir nesne geri verir, diğeri değer diğeri yapılır (boolean). Değer bir dizi, nesne vb. Olabilir.
Basit bir örnek:
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}
Ayrıca verim anahtar kelimesini anlamaya çalışıyorum. Mevcut anlayışımı temel alarak, jeneratörde, verim anahtar sözcüğü bir CPU bağlam anahtarı gibi çalışır. Verim ifadesi çalıştırıldığında, tüm durumlar (örneğin, yerel değişkenler) kaydedilir.
Bunun yanı sıra, {value: 0, done: false} gibi doğrudan bir sonuç nesnesi arayana döndürülecektir. Arayan bu sonuç nesnesini, next () öğesini çağırarak jeneratörü 'uyandırmaya' karar vermek için kullanabilir.
Bir başka önemli şey de yerel bir değişkene değer ayarlayabilmesidir. Bu değer, jeneratörü 'uyandırırken' 'next ()' arayan tarafından geçirilebilir. örneğin, it.next ('valueToPass'), şöyle: "sonuçDeğer = verim yavaşQuery (1);" Bir sonraki yürütmeyi uyandırdığı gibi, arayan da yürütmeye bazı çalışan sonuçlar verebilir (yerel değişkene enjekte ederek). Dolayısıyla, bu infaz için iki tür devlet vardır:
son yürütmede kaydedilen bağlam.
Bu yürütmenin tetikleyicisi tarafından enjekte edilen değerler.
Bu nedenle, bu özellik ile jeneratör birden fazla zaman uyumsuz işlemi sıralayabilir. İlk eşzamansız sorgunun sonucu, yerel değişken (yukarıdaki örnekte sonuçDeğeri) ayarlanarak ikincisine iletilir. İkinci eşzamansız sorgu yalnızca ilkinin eşzamansız sorgu yanıtıyla tetiklenebilir. Daha sonra ikinci async sorgusu, yerel değişken ilk sorgunun yanıtından enjekte edilen bir değer olduğundan, sonraki adımlara karar vermek için yerel değişken değerini kontrol edebilir.
Zaman uyumsuz sorguların zorlukları:
geri arama cehennemi
geri aramada parametre olarak geçmedikçe bağlam kaybı.
verim ve jeneratör her ikisinde de yardımcı olabilir.
Verim ve üreteç olmadan, birden fazla zaman uyumsuz sorguyu sıralamak, okunması ve bakımı kolay olmayan bağlam olarak parametrelerle iç içe geri arama gerektirir.
Aşağıda nodejs ile çalışan zincirleme zaman uyumsuz sorgu örneği:
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+++++++++++");
Çalışan sonuç aşağıdadır:
başlamak +++++++++++ +++++++++++
sorgu1 0
+++++++++++ uç +++++++++++
sorgu2 1
sorgu4 0
Aşağıdaki durum örneği yukarıdaki örnek için benzer bir şey yapabilir:
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+++++++++++");
Çalışan sonuç aşağıdadır:
başlamak +++++++++++ +++++++++++
sorgu1 0
+++++++++++ uç +++++++++++
sorgu2 1
sorgu4 0
jeneratör içinde döngü için çok yararlı 'jeneratör' sözdizimi unutmayın. Bir sonraki () işlevini kullanmaya gerek yoktur.
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);
}