Apa kata kunci hasil dalam JavaScript?


238

Saya mendengar tentang kata kunci "hasil" dalam JavaScript, tetapi saya menemukan dokumentasi yang sangat buruk tentang itu. Dapatkah seseorang menjelaskan saya (atau merekomendasikan situs yang menjelaskan) penggunaannya dan untuk apa digunakan?



4
itu dijelaskan dalam MDN , tapi saya pikir ini hanya berfungsi untuk firefox, kan? Seberapa portabel itu? Adakah cara untuk melakukan ini di Chrome atau node.js? PD: maaf, ini Javascript v1.7 + , jadi itu properti yang harus dilihat ketika mencari dukungan.
Trylks

1
@Trylks: Generator tersedia di Node sejak v0.11.2
Janus Troelsen

@JanusTroelsen Namun, hanya di belakang bendera. Mereka didukung secara asli di ioJS
Dan Pantry

Jawaban:


86

The Dokumentasi MDN cukup bagus, IMO.

Fungsi yang mengandung kata kunci hasil adalah generator. Ketika Anda menyebutnya, parameter formal terikat dengan argumen yang sebenarnya, tetapi tubuhnya tidak dievaluasi. Sebagai gantinya, generator-iterator dikembalikan. Setiap panggilan ke metode generator-iterator berikutnya () melakukan pass lain melalui algoritma iteratif. Nilai setiap langkah adalah nilai yang ditentukan oleh kata kunci hasil. Pikirkan hasil sebagai versi generator-iterator dari pengembalian, yang menunjukkan batas antara setiap iterasi algoritma. Setiap kali Anda memanggil next (), kode generator dilanjutkan dari pernyataan setelah hasil.


2
@NicolasBarbulesco ada contoh yang sangat jelas jika Anda mengklik ke dokumentasi MDN.
Matt Ball

@MattBall - apakah fungsi sebagai javascript untuk PI seperti ini cukup sebagai berikut: function * PI {PI = ((Math.SQRT8;) / 9801;); } - atau apakah sudah ada fungsi yang diimplementasikan dalam javascript untuk perhitungan PI ini?
dschinn1001

4
Apa gunanya mengutip MDN di sini? Saya pikir semua orang bisa membacanya di MDN. Kunjungi davidwalsh.name/promises untuk mempelajari lebih lanjut tentang mereka.
Ejaz Karim

20
Bagaimana hal ini mendapatkan ~ 80 upvotes ketika (a) itu adalah salinan dari "dokumentasi yang sangat buruk" sebagaimana penanya menyebutnya dan (b) ia mengatakan tidak ada yang membantu? Jawaban yang jauh lebih baik di bawah ini.
www-0av-Com

4
jika seseorang meminta penjelasan, hanya menyalin paste dokumentasi sama sekali tidak berguna. Bertanya berarti Anda sudah mencari dalam dokumen tetapi Anda tidak memahaminya.
Diego

205

Terlambat menjawab, mungkin semua orang tahu tentang yieldsekarang, tetapi beberapa dokumentasi yang lebih baik telah datang.

Mengadaptasi contoh dari "Javascript's Future: Generators" oleh James Long untuk standar Harmony resmi:

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

"Ketika kamu memanggil foo, kamu mendapatkan kembali objek Generator yang memiliki metode selanjutnya."

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

Begitu yieldjuga seperti return: Anda mendapatkan sesuatu kembali. return xmengembalikan nilai x, tetapi yield xmengembalikan fungsi, yang memberi Anda metode untuk beralih ke nilai berikutnya. Berguna jika Anda berpotensi menjalani prosedur intensif memori yang mungkin ingin Anda interupsi selama iterasi.


13
Bermanfaat, tapi saya kira Anda yang function* foo(x){ada di sana
Rana Deep

9
@RanaDeep: Sintaks fungsi diperluas untuk menambahkan token opsional * . Apakah Anda memerlukannya atau tidak, itu tergantung pada jenis masa depan yang Anda kembalikan. Detailnya panjang: GvR menjelaskannya untuk implementasi Python , di mana implementasi Javascript dimodelkan. Menggunakan function *akan selalu benar, meskipun dalam beberapa kasus sedikit lebih mahal daripada functiondengan yield.
Uskup

1
@ Ajedi32 Ya, Anda benar. Harmony menstandarisasi korelasi antara function *dan yield, dan menambahkan kesalahan yang dikutip ("Kesalahan awal dinaikkan jika ekspresi hasil atau hasil * terjadi dalam fungsi non-generator"). Tetapi, implementasi Javascript 1.7 asli di Firefox tidak memerlukan* . Jawaban diperbarui sesuai. Terima kasih!
Uskup

3
@MuhammadUmer Js akhirnya menjadi bahasa yang bisa Anda gunakan. Itu disebut evolusi.
Lukas Liesis

1
contoh berguna, tapi ... apa fungsi *?
Diego

66

Ini Sangat Sederhana, Ini cara kerjanya

  • yieldkata kunci hanya membantu untuk menjeda dan melanjutkan fungsi kapan saja secara tidak sinkron .
  • Selain itu membantu mengembalikan nilai dari fungsi generator .

Ambil fungsi generator sederhana ini :

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');
}

biarkan _process = proses ();

Sampai Anda memanggil _process.next () itu tidak akan mengeksekusi 2 baris kode pertama, maka hasil pertama akan menghentikan sementara fungsi. Untuk melanjutkan fungsi sampai titik jeda berikutnya ( menghasilkan kata kunci ) Anda perlu memanggil _process.next () .

Anda dapat berpikir banyak hasil adalah breakpoint dalam debugger javascript dalam satu fungsi. Sampai Anda memberitahu untuk menavigasi breakpoint berikutnya itu tidak akan menjalankan blok kode. ( Catatan : tanpa memblokir seluruh aplikasi)

Tetapi sementara yield melakukan jeda ini dan melanjutkan perilaku itu dapat mengembalikan beberapa hasil juga {value: any, done: boolean} sesuai dengan fungsi sebelumnya, kami belum mengeluarkan nilai apa pun. Jika kita mengeksplorasi output sebelumnya, hasilnya akan sama { value: undefined, done: false } dengan nilai yang tidak ditentukan .

Mari kita menggali kata kunci hasil. Secara opsional, Anda dapat menambahkan ekspresi dan menetapkan menetapkan nilai opsional default . (Sintaks dokumen resmi)

[rv] = yield [expression];

Ekspresi : Nilai untuk kembali dari fungsi generator

yield any;
yield {age: 12};

rv : Mengembalikan nilai opsional yang diteruskan ke metode generator berikutnya ()

Cukup Anda dapat melewatkan parameter ke proses () berfungsi dengan mekanisme ini, untuk menjalankan bagian hasil yang berbeda.

let val = yield 99; 

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

Coba sekarang

Penggunaan

  • Evaluasi malas
  • Urutan tak terbatas
  • Kontrol asinkron mengalir

Referensi:


54

Menyederhanakan / menguraikan jawaban Nick Sotiros (yang menurut saya hebat), saya pikir lebih baik untuk menggambarkan bagaimana seseorang akan mulai mengkodekan dengan yield .

Menurut pendapat saya, keuntungan terbesar dari menggunakan yield adalah akan menghilangkan semua masalah panggilan balik bersarang yang kita lihat dalam kode. Sulit untuk melihat bagaimana pada awalnya, itulah sebabnya saya memutuskan untuk menulis jawaban ini (untuk diri saya sendiri, dan semoga orang lain!)

Cara melakukannya adalah dengan memperkenalkan gagasan tentang rutinitas bersama, yang merupakan fungsi yang dapat secara sukarela menghentikan / berhenti sampai mendapatkan apa yang dibutuhkannya. Dalam javascript, ini dilambangkan dengan function*. Hanya function*fungsi yang dapat digunakan yield.

Inilah beberapa javascript khas:

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

Ini kikuk karena sekarang semua kode Anda (yang jelas perlu menunggu loadFromDBpanggilan ini ) harus berada di dalam panggilan balik yang tampak jelek ini. Ini buruk karena beberapa alasan ...

  • Semua kode Anda dimasukkan satu tingkat ke dalam
  • Anda memiliki tujuan ini })yang perlu Anda perhatikan di mana-mana
  • Semua function (err, result)jargon ekstra ini
  • Tidak jelas apakah Anda melakukan ini untuk menetapkan nilai result

Di sisi lain, dengan yield, semua ini dapat dilakukan dalam satu baris dengan bantuan kerangka kerja bersama yang bagus.

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

Dan sekarang fungsi utama Anda akan menghasilkan di mana diperlukan ketika perlu menunggu variabel dan hal-hal untuk memuat. Tetapi sekarang, untuk menjalankan ini, Anda perlu memanggil yang normal (fungsi non-coroutine). Kerangka kerja bersama sederhana dapat memperbaiki masalah ini sehingga yang harus Anda lakukan adalah menjalankan ini:

start(main())

Dan mulai didefinisikan (dari jawaban 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
        });
    }
}

Dan sekarang, Anda dapat memiliki kode cantik yang jauh lebih mudah dibaca, mudah dihapus, dan tidak perlu mengutak-atik indentasi, fungsi, dll.

Pengamatan yang menarik adalah bahwa dalam contoh ini, yieldsebenarnya hanya kata kunci yang dapat Anda tempatkan sebelum fungsi dengan panggilan balik.

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

Akan mencetak "Hello World". Jadi Anda benar-benar dapat mengubah fungsi callback menjadi menggunakan yieldhanya dengan membuat tanda tangan fungsi yang sama (tanpa cb) dan kembali function (cb) {}, seperti:

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

Semoga dengan pengetahuan ini Anda dapat menulis kode yang lebih bersih dan mudah dibaca yang mudah dihapus !


a function*hanya fungsi biasa tanpa hasil?
Abdul

Saya pikir maksud Anda itu function *adalah fungsi yang berisi hasil. Ini adalah fungsi khusus yang disebut generator.
Leander

7
Untuk orang-orang yang sudah menggunakan di yieldmana-mana, saya yakin ini lebih masuk akal daripada callback, tetapi saya gagal melihat bagaimana ini lebih mudah dibaca daripada callback.
palswim

artikel itu sulit dimengerti
Martian2049

18

Untuk memberikan jawaban yang lengkap: yieldbekerja serupa dengan return, tetapi dalam generator.

Adapun contoh yang umum diberikan, ini berfungsi sebagai berikut:

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

Tetapi ada juga tujuan kedua dari kata kunci hasil. Ini dapat digunakan untuk mengirim nilai ke generator.

Untuk memperjelas, contoh kecil:

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

Ini bekerja, sebagai nilai 2ditugaskan untuk y, dengan mengirimkan ke generator, setelah itu berhenti di hasil pertama (yang kembali 0).

Ini memungkinkan kita untuk melakukan beberapa hal yang sangat funky. (lihat coroutine)



6

yield juga dapat digunakan untuk menghilangkan panggilan balik neraka, dengan kerangka kerja coroutine.

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());

4

Generator urutan Fibonacci menggunakan kata kunci hasil.

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 

4

Yeild kata kunci dalam fungsi javaScript membuatnya menjadi generator,

Apa itu generator di javaScript?

Generator adalah fungsi yang menghasilkan urutan hasil alih-alih nilai tunggal, yaitu Anda menghasilkan serangkaian nilai

Arti generator membantu kita bekerja secara serempak dengan iterator bantuan, Oh sekarang apa iterator hack itu? Betulkah?

Iterator artinya kita dapat mengakses item satu per satu

dari mana iterator membantu kami mengakses item satu per satu? ini membantu kami mengakses item melalui fungsi generator,

fungsi generator adalah fungsi yang kami gunakan yeild kata kunci, menghasilkan kata kunci membantu kita dalam menjeda dan melanjutkan eksekusi fungsi

di sini adalah contoh cepat

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

izinkan saya menjelaskan apa yang sedang terjadi

Anda melihat eksekusi sedang dijeda pada setiap yeildkata kunci dan kami dapat mengaksesnya terlebih dahuluyield dengan bantuan iterator.next()

ini beralih ke semua yieldkata kunci satu per satu dan kemudian kembali tidak terdefinisi ketika tidak ada lagi yieldkata kunci yang tersisa dalam kata-kata sederhana yang dapat Anda ucapkanyield kata kunci adalah break point di mana fungsi setiap kali jeda dan hanya melanjutkan ketika menyebutnya menggunakan iterator

untuk kasus kami: _getMeDrink.next() ini adalah contoh iterator yang membantu kami mengakses setiap fungsi break point

Contoh Generator: async/await

jika Anda melihat implementasi async/await Anda akan melihat generator functions & promisesdigunakan untuk membuatasync/await pekerjaan

mohon tunjukkan saran yang diterima


3

Ketergantungan antara panggilan javascript async.

Contoh bagus lainnya tentang bagaimana hasil dapat digunakan.

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()


0

Sebelum Anda belajar tentang hasil, Anda perlu tahu tentang generator. Generator dibuat menggunakan function*sintaks. Fungsi generator tidak mengeksekusi kode melainkan mengembalikan jenis iterator yang disebut generator. Ketika nilai diberikan dengan menggunakan nextmetode, fungsi generator terus mengeksekusi sampai menemukan kata kunci hasil. Menggunakan yieldmemberi Anda kembali objek yang berisi dua nilai, satu adalah nilai dan yang lainnya dilakukan (boolean). Nilainya bisa berupa array, objek, dll.


0

Contoh sederhana:

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}

0

Saya juga mencoba memahami kata kunci hasil. Berdasarkan pemahaman saya saat ini, dalam generator, menghasilkan kata kunci berfungsi seperti saklar konteks-CPU. Ketika pernyataan hasil dijalankan, semua status (misalnya, variabel lokal) disimpan.

Selain itu, objek hasil langsung akan dikembalikan ke pemanggil, seperti {value: 0, done: false}. Penelepon dapat menggunakan objek hasil ini untuk memutuskan apakah akan 'membangkitkan' generator lagi dengan memanggil next () (memanggil next () adalah untuk mengulangi eksekusi).

Hal penting lainnya adalah ia dapat menetapkan nilai ke variabel lokal. Nilai ini dapat dilewati oleh pemanggil 'next ()' saat 'membangunkan' generator. misalnya, it.next ('valueToPass'), seperti ini: "resultValue = yield slowQuery (1);" Sama seperti ketika membangunkan eksekusi berikutnya, pemanggil dapat menyuntikkan beberapa hasil yang berjalan ke eksekusi (menyuntikkannya ke variabel lokal). Jadi, untuk eksekusi ini, ada dua jenis negara:

  1. konteks yang disimpan dalam eksekusi terakhir.

  2. Nilai yang disuntikkan oleh pemicu eksekusi ini.

Jadi, dengan fitur ini, generator dapat memilah beberapa operasi async. Hasil permintaan async pertama akan diteruskan ke yang kedua dengan menetapkan variabel lokal (resultValue dalam contoh di atas). Permintaan async kedua hanya dapat dipicu oleh respons permintaan async pertama. Kemudian permintaan async kedua dapat memeriksa nilai variabel lokal untuk memutuskan langkah selanjutnya karena variabel lokal adalah nilai yang disuntikkan dari respons permintaan pertama.

Kesulitan dari permintaan async adalah:

  1. panggilan balik neraka

  2. kehilangan konteks kecuali meneruskannya sebagai parameter dalam panggilan balik.

hasil dan generator dapat membantu keduanya.

Tanpa hasil dan generator, untuk memilah beberapa permintaan async memerlukan panggilan balik bersarang dengan parameter sebagai konteks yang tidak mudah dibaca dan dipelihara.

Di bawah ini adalah contoh permintaan async dirantai yang berjalan dengan 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+++++++++++");

Di bawah ini adalah hasil yang berjalan:

+++++++++++ mulai +++++++++++

query1 0

+++++++++++ end +++++++++++

query2 1

query4 0

Pola keadaan di bawah ini dapat melakukan hal serupa untuk contoh di atas:

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+++++++++++");

Berikut ini adalah hasil yang berjalan:

+++++++++++ mulai +++++++++++

query1 0

+++++++++++ end +++++++++++

query2 1

query4 0


0

jangan lupa sintaks 'x of generator' yang sangat membantu untuk loop melalui generator. Tidak perlu menggunakan fungsi next () sama sekali.

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);
}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.