Я слышал о ключевом слове yield в JavaScript, но нашел очень плохую документацию по нему. Может ли кто-нибудь объяснить мне (или порекомендовать сайт, который объясняет) его использование и для чего он используется?
Я слышал о ключевом слове yield в JavaScript, но нашел очень плохую документацию по нему. Может ли кто-нибудь объяснить мне (или порекомендовать сайт, который объясняет) его использование и для чего он используется?
Ответы:
Документация по 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
возвращает функцию, которая дает метод для итерации к следующему значению. Полезно, если у вас есть потенциально ресурсоемкая процедура, которую вы можете прервать во время итерации.
function* foo(x){
здесь
*
токен . Нужно вам это или нет, зависит от того, какое будущее вы возвращаете. Детали длинные: GvR объясняет это для реализации Python , на которой смоделирована реализация Javascript. Использование function *
всегда будет правильным, хотя в некоторых случаях немного больше накладных расходов, чем function
с yield
.
function *
и yield
и добавила указанную ошибку («Ранняя ошибка возникает, если в функции, не являющейся генератором, возникает выражение yield или yield *»). Но оригинальная реализация Javascript 1.7 в Firefox не требовала*
. Обновил ответ соответственно. Спасибо!
Это действительно просто, вот как это работает
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 ();
Пока вы не вызовете _process.next (), он не будет выполнять первые 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
Обычаи
Ссылки:
Упрощая / развивая ответ Ника Сотироса (который я считаю удивительным), я думаю, что лучше всего описать, как можно начать кодировать с 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)
}
}
Надеемся, что благодаря этим знаниям вы сможете написать более понятный и читаемый код, который легко удалить !
function*
обычная функция без выхода?
function *
это функция, которая содержит доходность. Это специальная функция, называемая генератором.
yield
везде, я уверен, что это имеет больше смысла, чем обратные вызовы, но я не вижу, как это более читабельно, чем обратные вызовы.
Чтобы дать полный ответ: 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
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
ключевые слова по одному, а затем возвращает неопределенное значение, когда больше не осталось yield
ключевых слов в простых словах, которые вы можете сказатьyield
ключевое слово является точкой останова, где функция каждый раз останавливается и возобновляется только при вызове с использованием итератора
для нашего случая: _getMeDrink.next()
это пример итератора, который помогает нам получить доступ к каждой точке останова в функции
Пример генераторов:
async/await
если вы видите реализацию async/await
вы увидите generator functions & promises
, используются, чтобы сделать async/await
работу
пожалуйста, укажите любые предложения приветствуется
Зависимость между асинхронными вызовами 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*
синтаксиса. Функции генератора не выполняют код, а вместо этого возвращают тип итератора, называемый генератором. Когда значение дается с помощью 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}
Я также пытаюсь понять ключевое слово yield. Исходя из моего текущего понимания, в генераторе ключевое слово yield работает как переключатель контекста процессора. Когда выполняется оператор yield, все состояния (например, локальные переменные) сохраняются.
Кроме того, объект прямого результата будет возвращен вызывающей стороне, например, {value: 0, done: false}. Вызывающий объект может использовать этот объект результата, чтобы решить, следует ли снова «разбудить» генератор, вызвав next () (вызов next () должен выполнить итерацию выполнения).
Еще одна важная вещь заключается в том, что он может установить значение для локальной переменной. Это значение может быть передано вызывающей функцией next () при «пробуждении» генератора. например, it.next ('valueToPass'), например: "resultValue = yield slowQuery (1);" Так же, как при пробуждении следующего выполнения, вызывающая сторона может ввести какой-либо результат выполнения в выполнение (вставив его в локальную переменную). Таким образом, для этого выполнения существует два вида состояния:
контекст, сохраненный в последнем исполнении.
Введенные значения триггером этого выполнения.
Таким образом, с помощью этой функции генератор может сортировать несколько асинхронных операций. Результат первого асинхронного запроса будет передан второму путем установки локальной переменной (resultValue в примере выше). Второй асинхронный запрос может быть вызван только ответом первого асинхронного запроса. Затем второй асинхронный запрос может проверить значение локальной переменной, чтобы решить следующие шаги, поскольку локальная переменная является введенным значением из ответа первого запроса.
Трудности асинхронных запросов:
ад обратного вызова
потеря контекста, если не передать их в качестве параметров в обратном вызове.
yield и генератор могут помочь в обоих случаях.
Без 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+++++++++++");
Ниже приведен результат выполнения:
+++++++++++ начать +++++++++++
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+++++++++++");
Ниже приводится результат выполнения:
+++++++++++ начать +++++++++++
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);
}