Що таке ключове слово в JavaScript?


238

Я чув про ключове слово "вихід" у JavaScript, але знайшов дуже погану документацію про нього. Чи може хтось пояснити мені (або порекомендувати сайт, який пояснює) його використання та для чого він використовується?


Він , ймовірно , означає «вихід» bytes.com/topic/python/answers/685510-yield-keyword-usage
мураха

4
це пояснено в MDN , але я думаю, що це працює лише для firefox, правда? Наскільки він портативний? Будь-який шлях до цього на Chrome або node.js? PD: вибачте, це Javascript v1.7 + , тому це властивість слід шукати, шукаючи підтримки.
Trylks

1
@Trylks: Генератори доступні в Node з v0.11.2
Janus Troelsen

@JanusTroelsen, однак, лише за прапором. Вони підтримуються вродженими в ioJS
Dan Pantry

Відповіді:


86

Документація MDN дуже хороша, IMO.

Функція, що містить ключове слово дохід, - це генератор. Коли ви називаєте це, його формальні параметри пов'язані з фактичними аргументами, але його тіло фактично не оцінюється. Натомість повертається генератор-ітератор. Кожен виклик наступного () методу генератора-ітератора виконує ще один прохід через ітераційний алгоритм. Значення кожного кроку - це значення, визначене ключовим словом урожайності. Подумайте про вихід як генератор-ітератор версії повернення, вказуючи межу між кожною ітерацією алгоритму. Кожен раз, коли ви викликаєте next (), код генератора відновлюється з оператора, що відповідає виходу.


2
@NicolasBarbulesco є дуже очевидний приклад, якщо ви перейдете до документації MDN.
Метт Бал

@MattBall - чи буде такою функцією як javascript для PI, як цього достатньо: функція * PI {PI = ((Math.SQRT8;) / 9801;); } - чи вже існує функція, реалізована в JavaScript для цього обчислення PI?
dschinn1001

4
Який сенс цитувати тут MDN? Я думаю, що кожен може прочитати це на MDN. Відвідайте davidwalsh.name/promises, щоб дізнатися більше про них.
Еджаз Карим

20
Як це отримало ~ 80 надбавок, коли (a) це копія "дуже поганої документації", як називає її запитувач, і (b) вона не каже нічого корисного? Набагато кращі відповіді нижче.
www-0av-Com

4
якщо хтось просить пояснення, просто скопіювати вставку документації зовсім не корисно. Запитувати означає, що ви вже шукали в документах, але ви їх не розуміли.
Дієго

205

Пізня відповідь, напевно, про це знають yieldуже всі, але прийшла якась краща документація.

Адаптація прикладу з "Майбутнього Javascript: Генератори" Джеймса Лонга для офіційного стандарту Гармонія:

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

"Коли ви викликаєте foo, ви отримуєте назад об'єкт Generator, який має наступний метод."

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

Так yieldце схоже return: ви щось отримуєте назад. return xповертає значення x, але yield xповертає функцію, яка дає вам метод перейти до наступного значення. Корисно, якщо у вас є потенційно об'ємна пам'ять , яку, можливо, ви хочете перервати під час ітерації.


13
Корисно, але я здогадуюсь, що ви його function* foo(x){там
Рана Глибока

9
@RanaDeep: Синтаксис функції розширено, щоб додати необов'язковий * маркер . Вам це потрібно чи ні, залежить від того, яке майбутнє ви повернетесь. Деталі довгі: GvR пояснює це для реалізації Python , за яким моделюється реалізація Javascript. Використання function *завжди буде правильним, хоча в деяких випадках трохи більше накладних, ніж functionз yield.
єпископ

1
@ Ajedi32 Так, ти маєш рацію. Гармонія стандартизувала кореляцію між function *і yield, і додала цитовану помилку ("Рання помилка підвищується, якщо вираз" урожай "або" вихід "має місце у негенераторній функції"). Але оригінальна реалізація Javascript 1.7 у Firefox не вимагала цього* . Відповідно оновлена ​​відповідь. Дякую!
єпископ

3
@MuhammadUmer Js нарешті стає мовою, якою ви можете фактично користуватися. Це називається еволюцією.
Лукас Лейсіс

1
Приклад корисний, але ... що таке функція *?
Дієго

66

Це дуже просто, ось як це працює

  • yieldключове слово просто допомагає призупинити і відновити функцію в будь-який час асинхронно .
  • Крім того, це допомагає повернути значення з функції генератора .

Візьміть цю просту функцію генератора :

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

нехай _process = process ();

До тих пір поки ви не викличете _process.next () це звичка виконувати в перші 2 рядки коду, то перший вихід буде призупинити функцію. Щоб відновити функцію до наступної точки паузи ( ключове слово вихід ), вам потрібно зателефонувати _process.next () .

Ви можете думати кілька виходів є точками зупинки в відладчик JavaScript в межах однієї функції. Поки ви не скажете орієнтуватися на наступній точці розриву, вона не буде виконувати блок коду. ( Примітка : не блокуючи всю програму)

Але хоча прибутковість виконує цю паузу і відновлення поведінки, вона може також повернути деякі результати , {value: any, done: boolean} відповідно до попередньої функції, ми не отримуємо жодних значень. Якщо ми вивчимо попередній вихід, він покаже те саме, що не визначене{ value: undefined, done: false } значення .

Дозволяє викопати ключове слово дохід. За бажанням можна додати вираз і встановити присвоєння необов'язкового значення за замовчуванням . (Офіційний синтаксис doc)

[rv] = yield [expression];

вираз : значення для повернення з функції генератора

yield any;
yield {age: 12};

rv : Повертає необов'язкове значення, яке перейшло до наступного () методу генератора

Просто ви можете передавати параметри для обробки () функції за допомогою цього механізму, для виконання різних частин продуктивності.

let val = yield 99; 

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

Спробуй зараз

Використання

  • Ледача оцінка
  • Нескінченні послідовності
  • Асинхронні потоки управління

Список літератури:


54

Спрощуючи / розробляючи відповідь Ніка Сотіроса (що, на мою думку, є приголомшливим), я думаю, що найкраще описати, як би почати кодування yield.

На мою думку, найбільшою перевагою використання yieldє те, що це усуне всі вкладені проблеми із зворотним викликом, які ми бачимо в коді. Спочатку важко зрозуміти, як спочатку, саме тому я вирішив написати цю відповідь (для себе, і, сподіваюся, інших!)

Це робиться шляхом введення ідеї спільного розпорядку, яка є функцією, яка може добровільно зупинити / призупинити, поки не отримає те, що їй потрібно. У javascript це позначається символом function*. function*Користуватися можуть лише функції yield.

Ось кілька типових javascript:

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

Це незграбно, тому що тепер увесь ваш код (якому, очевидно, потрібно чекати цього loadFromDBдзвінка) повинен знаходитися всередині цього некрасивого зворотного виклику. Це погано з кількох причин ...

  • Весь ваш код з відступом на один рівень у
  • У вас є цей кінець, про })який потрібно стежити всюди
  • Все це зайвий function (err, result)жаргон
  • Не зовсім зрозуміло, що ви робите це, щоб призначити значення result

З іншого боку, за допомогою цього yieldвсе це можна зробити в один рядок за допомогою приємного спільного розпорядку.

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

І тому ваша основна функція поступатиметься там, де потрібно, коли їй потрібно чекати завантаження змінних та речей. Але тепер, щоб запустити це, вам потрібно зателефонувати у звичайну (функція, яка не використовується). Проста спільна рутинна рамка може виправити цю проблему, щоб все, що вам потрібно зробити, це виконати:

start(main())

І початок визначено (з відповіді Ніка Сотіро)

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

А тепер ви можете мати гарний код, який набагато легше читається, легко видаляється та не потрібно поспішати з відступами, функціями тощо.

Цікавим зауваженням є те, що в цьому прикладі yieldнасправді є лише ключовим словом, яке ви можете поставити перед функцією із зворотним дзвоном.

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

Було б надруковано "Hello World". Таким чином, ви можете фактично перетворити будь-яку функцію зворотного виклику в використання yield, просто створивши ту саму підпис функції (без cb) і повернувшись function (cb) {}так:

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

Сподіваємось, що за допомогою цих знань ви зможете написати більш чистий, читабельний код, який легко видалити !


a function*- це лише звичайна функція без виходу?
Абдул

Я думаю, ви маєте на увазі, що function *це функція, яка містить врожайність. Це спеціальна функція, яка називається генератором.
Leander

7
Для людей, які вже користуються yieldскрізь, я впевнений, що це має більше сенсу, ніж зворотні дзвінки, але я не бачу, як це можна прочитати більше, ніж зворотні дзвінки.
palswim

цю статтю важко зрозуміти
Martian2049

18

Щоб дати повну відповідь: yieldпрацює аналогічно return, але в генераторі.

Що стосується поширеного прикладу, то він працює наступним чином:

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

Але це також друга мета ключового слова прибутковості. Його можна використовувати для надсилання значень генератору.

Для уточнення, невеликий приклад:

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

Це працює, як 2присвоюється значення y, надсилаючи його до генератора, після того, як воно зупинилося на першому виході (який повернувся 0).

Це дає нам змогу зробити справді цікаві речі. (шукати порядок)


16

Він використовується для ітераторів-генераторів. В основному це дозволяє зробити (потенційно нескінченну) послідовність, використовуючи процедурний код. Дивіться документацію Mozilla .


6

yield може також використовуватися для усунення пекло зворотного виклику, з рамкою підпрограми.

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

Генератор послідовностей Фібоначчі за допомогою ключового слова дохід.

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 ключове слово у функції javaScript робить його генератором,

що таке генератор в javaScript?

Генератор - це функція, яка виробляє послідовність результатів замість одного значення, тобто ви генеруєте ряд значень

Значить генератори допомагають нам працювати асинхронно за допомогою ітераторів допомоги. О, що ж таке ітератори хак? справді?

Ітератори - це засіб, за допомогою якого ми маємо можливість отримати доступ до предметів один за одним

звідки ітератор допомагає нам отримувати доступ до предмета один за одним? це допомагає нам отримувати доступ до елементів через функції генератора,

Функції генератора - це ті, в яких ми використовуємо yeildключове слово, ключове слово прибутковість допомагає нам призупинити та відновити виконання функції

ось короткий приклад

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

дозвольте мені по-блискучому пояснити, що відбувається

Ви помітили, що виконання призупиняється для кожного yeildключового слова, і ми можемо отримати доступ до нього yieldза допомогою ітератора.next()

це повторюється за всіма yieldключовими словами по одному, а потім повертається невизначеним, коли більше yieldпростих слів не залишилося ключових слів, ви можете сказати, що yieldключове слово - точка перерви, де функція кожного разу призупиняється і поновлюється лише під час виклику за допомогою ітератора

для нашого випадку: _getMeDrink.next()це приклад ітератора, який допомагає нам отримувати доступ до кожної точки перерви у функції

Приклад генераторів: async/await

якщо ви бачите реалізацію, async/await ви будете generator functions & promisesвикористовувати, щоб зробити async/awaitроботу

будь ласка, вкажіть, будь-які пропозиції вітаються


3

Залежність між викликами async javascript.

Ще один хороший приклад того, як можна використовувати урожай.

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

Перш ніж дізнатися про врожайність, потрібно знати про генератори. Генератори створюються за допомогою function*синтаксису. Функції генератора не виконують код, а натомість повертають тип ітератора, який називається генератором. Коли значення задається за допомогою nextметоду, функція генератора продовжує виконувати, поки не натикається на ключове слово дохідності. Використання yieldповертає об'єкт, що містить два значення, одне - значення, а друге - булеве. Значення може бути масивом, об'єктом тощо.


0

Простий приклад:

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

Я також намагаюся зрозуміти ключове слово дохідності. Виходячи з мого теперішнього розуміння, в генераторі ключове слово "вихід" працює як контекстний комутатор процесора. Під час запуску оператора дохідності всі стани (наприклад, локальні змінні) зберігаються.

Крім цього, об'єкт прямого результату буде повернутий абоненту, наприклад {value: 0, done: false}. Абонент може використовувати цей об'єкт результату, щоб вирішити, чи потрібно знову "розбудити" генератор, викликавши next () (виклик next () - це повторити виконання).

Ще одна важлива річ - це те, що вона може встановити значення для локальної змінної. Це значення може передавати абонент 'next ()', коли 'прокидається' генератор. наприклад, it.next ('valueToPass'), як-от так: "resultValue = привести slowQuery (1);" Як і під час пробудження наступного виконання, абонент може ввести деякий запущений результат до виконання (введення його в локальну змінну). Таким чином, для цього виконання існує два види держави:

  1. контекст, який збережено в останньому виконанні.

  2. Введені значення за допомогою тригера виконання.

Отже, за допомогою цієї функції генератор може сортувати кілька операцій з асинхронізацією. Результат першого запиту на асинхронізацію буде передано другому, встановивши локальну змінну (resultValue у наведеному вище прикладі). Другий запит на асинхронізацію може запускати лише відповідь першого запиту на асинхронізацію. Тоді другий запит на асинхронізацію може перевірити значення локальної змінної для вирішення наступних кроків, оскільки локальна змінна є введеним значенням з відповіді першого запиту.

Труднощі асинхронних запитів:

  1. зворотний виклик пекла

  2. втрата контексту, якщо не передавати їх як параметри зворотного дзвінка.

вихід і генератор можуть допомогти і в обох.

Без урожайності та генератора для сортування декількох запитів на асинхронізацію потрібен вкладений зворотний зв'язок з параметрами як контекст, який не легко читати та підтримувати.

Нижче наведений приклад ланцюгових запитів асинхронізації, який працює з 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+++++++++++");

Нижче наведено поточний результат:

+++++++++++ start ++++++++++++

запит1 0

+++++++++++++++++++++++++

запит2 1

query4 0

Нижче за схемою стану можна зробити подібне для наведеного вище прикладу:

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

Наведений нижче результат:

+++++++++++ start ++++++++++++

запит1 0

+++++++++++++++++++++++++

запит2 1

query4 0


0

не забувайте дуже корисний синтаксис 'x of generator' для проходження через генератор. Наступна () функція взагалі не потрібна.

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);
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.