Что такое ключевое слово yield в JavaScript? - PullRequest
205 голосов
/ 17 февраля 2010

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

Ответы [ 9 ]

175 голосов
/ 31 декабря 2013

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

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

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 возвращает функцию, которая дает метод для итерации к следующему значению. Полезно, если у вас есть потенциально требовательная к памяти процедура , которую вы, возможно, захотите прервать во время итерации.

81 голосов
/ 17 февраля 2010

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

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

52 голосов
/ 16 марта 2016

Упрощая / развивая ответ Ника Сотироса (который я считаю удивительным), я думаю, что лучше всего описать, как можно начать кодирование с 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')
}

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

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

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

46 голосов
/ 31 января 2017

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

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

let _process = process();

let out1 = _process.next();
console.log(out1);

let out2 = _process.next();
console.log(out2);

let out3 = _process.next(90);
console.log(out3);

let _process = process ();

Пока вы не вызовете _process.next () it wont , выполните первые 2 строки кода, затем первый выход будет пауза функция. Чтобы возобновить функцию до следующей паузы точки ( yield ключевое слово ) вам необходимо вызвать _process.next ( ) .

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

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

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

[rv] = yield [expression];

выражение : значение, возвращаемое из функции генератора

yield any;
yield {age: 12};

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

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

let val = yield 99; 

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

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

Обычаи

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

Ссылки:

17 голосов
/ 14 сентября 2014

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

Это позволяет нам делать действительно забавные вещи. (ищите сопрограмму)

16 голосов
/ 17 февраля 2010

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

.
6 голосов
/ 04 ноября 2014

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 голосов
/ 22 марта 2018

Генератор последовательности Фибоначчи с использованием ключевого слова 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 
3 голосов
/ 06 марта 2019

Зависимость между асинхронными вызовами 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()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...