Какое ключевое слово yield в JavaScript?
Я слышал о ключевом слове "yield" в JavaScript, но я нашел очень плохую документацию об этом. Может ли кто-нибудь объяснить мне (или рекомендовать сайт, который объясняет) его использование и то, для чего он используется?
Ответы
Ответ 1
документация MDN довольно хороша, IMO.
Функция, содержащая ключевое слово yield, является генератором. Когда вы вызываете его, его формальные параметры привязаны к фактическим аргументам, но его тело фактически не оценивается. Вместо этого возвращается генератор-итератор. Каждый вызов метода generator-iterator next() выполняет другой проход через итеративный алгоритм. Каждое значение шага - это значение, указанное ключевым словом yield. Подумайте о выходе как версии возврата-генератора-итератора, указав границу между каждой итерацией алгоритма. Каждый раз, когда вы вызываете next(), код генератора возобновляется из инструкции, следующей за выходом.
Ответ 2
Поздний ответ, вероятно, все знают о yield
сейчас, но появилась хорошая документация.
Адаптация примера от "Javascript Future: Generators" Джеймса Лонга для официального стандарта 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
возвращает функцию, которая дает вам метод для итерации к следующему значению. Полезно, если у вас есть потенциально интенсивная память, которую вы могли бы прервать во время итерации.
Ответ 3
Упрощая/развивая ответ Ника Сотироса (который я считаю удивительным), я думаю, что лучше всего описать, как можно начать кодирование с 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)
}
}
Надеемся, что благодаря этим знаниям вы сможете написать более понятный и читаемый код, который легко удалить !
Ответ 4
Это действительно просто, вот как это работает
- Ключевое слово
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);
Ответ 5
Чтобы дать полный ответ: 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
).
Это позволяет нам по-настоящему напуганные вещи. (найдите coroutine)
Ответ 6
Он используется для итератор-генераторов. В принципе, это позволяет вам сделать (потенциально бесконечную) последовательность, используя процедурный код. См. Документация Mozilla.
Ответ 7
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());
Ответ 8
Генератор последовательности Фибоначчи с использованием ключевого слова 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
Ответ 9
Зависимость между асинхронными вызовами 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()