Что такое ключевое слово yield в JavaScript?
Я слышал о ключевом слове yield в JavaScript, но нашел очень плохую документацию по нему. Может кто-нибудь объяснить мне (или порекомендовать сайт, который объясняет) его использование и для чего он используется?
15 ответов
Документация по MDN довольно хорошая, IMO.
Функция, содержащая ключевое слово yield, является генератором. Когда вы вызываете его, его формальные параметры привязываются к фактическим аргументам, но его тело фактически не оценивается. Вместо этого возвращается генератор-итератор. Каждый вызов метода next() генератора-итератора выполняет очередной проход через итерационный алгоритм. Значение каждого шага - это значение, указанное в ключевом слове yield. Думайте о yield как о генерато-итераторной версии возврата, указывающей границу между каждой итерацией алгоритма. Каждый раз, когда вы вызываете next(), код генератора возобновляется с оператора, следующего за yield.
Поздний ответ, наверное, все знают о 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
возвращает функцию, которая дает метод для итерации к следующему значению. Полезно, если у вас есть потенциально ресурсоемкая процедура, которую вы, возможно, захотите прервать во время итерации.
Это действительно просто, вот как это работает
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');
}
let _process = process();
До тех пор, пока вы не вызовете _process.next(), он не будет выполнять первые 2 строки кода, а затем первый выход приведет к приостановке функции. Чтобы возобновить работу функции до следующей точки паузы (ключевое слово yield), вам нужно вызвать _process.next().
Вы можете думать, что несколько выходов являются точками останова в отладчике javascript внутри одной функции. Пока вы не скажете перейти к следующей точке останова, он не будет выполнять блок кода. (Примечание: без блокировки всего приложения)
Но хотя yield выполняет эту паузу и возобновляет поведение, он также может возвращать некоторые результаты. {value: any, done: boolean}
в соответствии с предыдущей функцией мы не излучали никаких значений. Если мы рассмотрим предыдущий вывод, он покажет то же самое { value: undefined, done: false }
со значением не определено.
Давайте перейдем к ключевому слову 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
Обычаи
- Ленивая оценка
- Бесконечные последовательности
- Асинхронные потоки управления
Рекомендации:
Упрощая / развивая ответ Ника Сотироса (который я считаю удивительным), я думаю, что лучше всего описать, как можно начать кодировать с 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)
}
}
Надеемся, что благодаря этим знаниям вы сможете написать более понятный и читаемый код, который легко удалить!
Yeild
ключевое слово в функции javaScript делает его генератором,
что такое генератор в javaScript?
Генератор - это функция, которая производит последовательность результатов вместо одного значения, т.е. вы генерируете серию значений.
Генераторы смысла помогают нам работать асинхронно с помощью итераторов помощи. А теперь, что такое итераторы взлома? действительно?
Итераторы - это средства, с помощью которых мы можем получать доступ к элементам по одному за раз
откуда итератор помогает нам получать доступ к элементу по одному? это помогает нам получать доступ к элементам через функции генератора,
функции генератора - это те, в которых мы используем yeild
ключевое слово, ключевое слово yield помогают нам приостанавливать и возобновлять выполнение функции
вот быстрый пример
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
ключевые слова по одному, а затем возвращает undefined, когда больше нет yield
ключевые слова оставлены простыми словами, которые можно сказать yield
ключевое слово - это точка останова, где функция каждый раз приостанавливается и возобновляется только при ее вызове с помощью итератора
для нашего случая: _getMeDrink.next()
это пример итератора, который помогает нам получить доступ к каждой точке останова в функции
Пример генераторов:
async/await
если вы видите реализацию async/await
ты увидишь generator functions & promises
используются для изготовления async/await
Работа
пожалуйста, укажите, что любые предложения приветствуются
Чтобы дать полный ответ: 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
).
Это позволяет нам делать действительно интересные вещи. (посмотрите на сопрограмму)
Используется для генераторов итераторов. По сути, это позволяет вам создать (потенциально бесконечную) последовательность, используя процедурный код. Смотрите документацию Mozilla.
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());
Генератор последовательности Фибоначчи с использованием ключевого слова 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
Зависимость между асинхронными вызовами 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()
привет, если вы дошли до этой точки, это означает, что все предыдущие ответы не оправдали ваших ожиданий, поэтому я облегчу вам понимание...
function * y(){
const r = yield;
/** program continue after next() call... **/
}
так что сначала...
- y() НЕ является функцией, это ГЕНЕРАТОР.
- Генераторы называются генераторами, потому что они могут генерировать несколько возвратов.
- генератор приостанавливает выполнение при обнаружении «выходности».
- вы можете добавить столько урожая, сколько вам нужно.
- весь код после выхода не будет выполняться до вызова next(см. ниже).
- генераторы можно использовать с async/await.
- генераторы, в отличие от функций, могут иметь число x или возвращать значения.
- вызов выхода аналогичен вызову возврата.
- вы получаете значение от доходности со свойством .value... (см. ниже).
- помните, что первый вызов next() просто достигнет доходности и будет ждать следующего вызова «next()», где вы действительно что-то обрабатываете, так что... call.next() достигнет точки останова и следующего вызова. next('что-то здесь') сделает что-то после паузы выполнения...
САМЫЙ МИНИМАЛИСТИЧНЫЙ ПРИМЕР
function * f(){
const u = yield; // pause here ...
return u; // something with u after 2nd next() call (see below)
}
const call = f();
call.next(); // will get to yield(first yield) and wait
call.next('wow'); // as we have 1 yield in the generator, the second next() call will reach the return point where you will get 'wow' as you passed a string 'wow' as the second next() call argument, you can obviously pass in whatever you want(callbacks, string, objects...)
надеюсь, что я изложил ясно и легко понять... помните, что есть официальная документация MDN, которую вы можете найти по адресу https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield
Я также пытаюсь понять ключевое слово yield. Исходя из моего текущего понимания, в генераторе ключевое слово yield работает как переключатель контекста ЦП. При запуске оператора yield все состояния (например, локальные переменные) сохраняются.
Кроме того, вызывающей стороне будет возвращен объект прямого результата, например { value: 0, done: false }. Вызывающий может использовать этот объект результата, чтобы решить, нужно ли снова "разбудить" генератор, вызвав next() (вызов next () - это итерация выполнения).
Еще одна важная вещь - это то, что он может устанавливать значение локальной переменной. Это значение может быть передано вызывающей функцией next () при "пробуждении" генератора. например, it.next('valueToPass'), например: "resultValue = yield slowQuery(1);" Точно так же, как при пробуждении следующего выполнения, вызывающий может ввести некоторый результат выполнения в выполнение (вставляя его в локальную переменную). Таким образом, для этого исполнения существует два вида состояний:
контекст, сохраненный при последнем выполнении.
Значения, введенные триггером этого выполнения.
Таким образом, с помощью этой функции генератор может выполнять несколько асинхронных операций. Результат первого асинхронного запроса будет передан второму путем установки локальной переменной (resultValue в приведенном выше примере). Второй асинхронный запрос может быть запущен только ответом на первый асинхронный запрос. Затем второй асинхронный запрос может проверить значение локальной переменной, чтобы принять решение о следующих шагах, поскольку локальная переменная является введенным значением из ответа на первый запрос.
Сложности асинхронных запросов:
обратный вызов ад
потеря контекста, если они не передаются в качестве параметров в обратном вызове.
yield и generator могут помочь и в том, и в другом.
Без yield и генератора для сортировки нескольких асинхронных запросов требуется вложенный обратный вызов с параметрами в качестве контекста, который нелегко читать и поддерживать.
Ниже приведен пример связанных асинхронных запросов, работающих с 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 +++++++++++
query1 0
+++++++++++ конец +++++++++++
query2 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 +++++++++++
query1 0
+++++++++++ конец +++++++++++
query2 1
query4 0
Не забывайте очень полезный синтаксис "x of generator" для циклического перебора генератора. Нет необходимости использовать функцию next() вообще.
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);
}
Прежде чем вы узнаете об урожайности, вам нужно узнать о генераторах. Генераторы создаются с использованиемfunction*
синтаксис. Функции генератора не выполняют код, а вместо этого возвращают тип итератора, называемый генератором. Когда значение задается с помощьюnext
, функция генератора продолжает выполняться, пока не встретит ключевое слово yield. С помощьюyield
возвращает вам объект, содержащий два значения, одно из которых является значением, а другое - готовым (логическим). Значение может быть массивом, объектом и т. Д.
Простой пример:
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}