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


Я слышал о ключевом слове "yield" в JavaScript, но я нашел очень плохую документацию об этом. Может кто-нибудь объяснить мне (или порекомендовать сайт, который объясняет) его использование и для чего он используется?

8 174

8 ответов:

The документация MDN довольно хорошо, ИМО.

функция, содержащая ключевое слово yield, является генератором. Когда вы вызываете его, его формальные параметры привязаны к фактическим аргументам, но его тело фактически не оценивается. Вместо этого возвращается генератор-итератор. Каждый вызов метода Next () генератора-итератора выполняет еще один проход через итерационный алгоритм. Значение каждого шага-это значение, заданное ключевым словом yield. Думайте о доходности как генератор-итератор версии return, указывающий границу между каждой итерацией алгоритма. Каждый раз, когда вы вызываете next (), код генератора возобновляется из оператора, следующего за yield.

поздний ответ, наверное, все знают о yield теперь, но некоторые лучшие документы пришли вместе.

адаптация примера от "будущее Javascript: генераторы" Джеймс Лонг для официального стандарта гармонии:

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

" когда вы вызываете foo, вы получаете обратно объект генератора, который имеет следующий метод."

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

так yield вроде как return: вы получаете что-то взамен. return x возвращает значение x, а yield x возвращает функцию, которая дает вам способ, чтобы выполнить итерации к следующему значению. Полезно, если у вас есть потенциально интенсивная процедура памяти что вы можете прервать во время итерации.

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

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

как это делается, вводя идею a co-routine, которая является функцией, которая может добровольно остановить / приостановить, пока она не получит то, что ей нужно. В javascript это обозначается как function*. Только function* функции можно использовать yield.

вот некоторые типичные javascript:

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

это неуклюже, потому что теперь весь ваш код (который, очевидно, должен ждать этого loadFromDB call)должен быть внутри этого уродливого обратного вызова. Это плохо по нескольким причинам...

  • весь ваш код с отступом один уровень в
  • у вас есть этот конец }) который вам нужно отслеживать везде
  • все это лишний function (err, result) жаргон
  • не совсем ясно, что вы делаете это, чтобы присвоить значение result

С другой стороны, с yield, все это можно сделать в одна строка с помощью приятной совместной рутинной структуры.

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

и поэтому теперь ваша основная функция будет давать там, где это необходимо, когда он должен ждать переменных и вещей, чтобы загрузить. Но теперь, чтобы запустить это, вам нужно вызвать a нормальный (функция non-coroutine). Простая структура совместной работы может решить эту проблему, так что все, что вам нужно сделать, это запустить это:

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

надеюсь, с этим знанием вы можете написать более чистый, более читаемый код, который является легко удалить!

это действительно просто, вот как это работает

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

    yield;

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

давайте _process = process ();

пока ты не позвонишь _процесс.далее () это не выполнить первые 2 строки кода, то первый выход будет пауза функции. К резюме функции до следующего пауза точка (ключевое слово yield) вам нужно позвонить _процесс.далее ().

вы можете думать несколько доходность это точки останова в отладчике javascript в пределах одной функции. До вы говорите, чтобы перейти к следующей точке останова он не будет выполнять код блок. (Примечание: без блокировки всего приложения)

но пока yield выполняет эту паузу и возобновляет поведение, оно может вернуть некоторые результаты а также {value: any, done: boolean} в соответствии с предыдущей функцией мы не выделяем никаких значений. Если мы исследуем предыдущий вывод, он покажет то же самое { value: undefined, done: false } со значением undefined.

давайте копать в Ключевое слово yield. При желании вы можете добавить выражение и set присвоить дополнительное значение по умолчанию. (Официальный синтаксис док)

[rv] = yield [expression];

выражение: значение для возврата из функции генератора

yield any;
yield {age: 12};

rv: возвращает необязательное значение, переданное следующему генератору() метод

просто вы можете передать параметры функции process () с помощью этого механизма, чтобы выполнить различные части выхода.

let val = yield 99; 

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

Попробуйте Сейчас

обычаи

  • ленивый оценке
  • бесконечные последовательности
  • асинхронным управлением потоки

ссылки:

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

чтобы дать полный ответ: 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

но есть и вторая цель ключевого слова yield. Он может быть использован для отправки значений в генератор.

чтобы уточнить, небольшой пример:

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

это позволяет нам к некоторым действительно фанки вещи. (посмотрите сопрограмма)

yield также может быть использован для устранения обратного вызова hell,с сопрограммой.

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

генератор последовательности Фибоначчи с использованием ключевого слова yield.

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