Я чув про ключове слово "вихід" у JavaScript, але знайшов дуже погану документацію про нього. Чи може хтось пояснити мені (або порекомендувати сайт, який пояснює) його використання та для чого він використовується?
Я чув про ключове слово "вихід" у JavaScript, але знайшов дуже погану документацію про нього. Чи може хтось пояснити мені (або порекомендувати сайт, який пояснює) його використання та для чого він використовується?
Відповіді:
Документація MDN дуже хороша, IMO.
Функція, що містить ключове слово дохід, - це генератор. Коли ви називаєте це, його формальні параметри пов'язані з фактичними аргументами, але його тіло фактично не оцінюється. Натомість повертається генератор-ітератор. Кожен виклик наступного () методу генератора-ітератора виконує ще один прохід через ітераційний алгоритм. Значення кожного кроку - це значення, визначене ключовим словом урожайності. Подумайте про вихід як генератор-ітератор версії повернення, вказуючи межу між кожною ітерацією алгоритму. Кожен раз, коли ви викликаєте next (), код генератора відновлюється з оператора, що відповідає виходу.
Пізня відповідь, напевно, про це знають yield
уже всі, але прийшла якась краща документація.
Адаптація прикладу з "Майбутнього Javascript: Генератори" Джеймса Лонга для офіційного стандарту Гармонія:
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
, і додала цитовану помилку ("Рання помилка підвищується, якщо вираз" урожай "або" вихід "має місце у негенераторній функції"). Але оригінальна реалізація 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');
}
нехай _process = process ();
До тих пір поки ви не викличете _process.next () це звичка виконувати в перші 2 рядки коду, то перший вихід буде призупинити функцію. Щоб відновити функцію до наступної точки паузи ( ключове слово вихід ), вам потрібно зателефонувати _process.next () .
Ви можете думати кілька виходів є точками зупинки в відладчик JavaScript в межах однієї функції. Поки ви не скажете орієнтуватися на наступній точці розриву, вона не буде виконувати блок коду. ( Примітка : не блокуючи всю програму)
Але хоча прибутковість виконує цю паузу і відновлення поведінки, вона може також повернути деякі результати , {value: any, done: boolean}
відповідно до попередньої функції, ми не отримуємо жодних значень. Якщо ми вивчимо попередній вихід, він покаже те саме, що не визначене{ value: undefined, done: false }
значення .
Дозволяє викопати ключове слово дохід. За бажанням можна додати вираз і встановити присвоєння необов'язкового значення за замовчуванням . (Офіційний синтаксис doc)
[rv] = yield [expression];
вираз : значення для повернення з функції генератора
yield any;
yield {age: 12};
rv : Повертає необов'язкове значення, яке перейшло до наступного () методу генератора
Просто ви можете передавати параметри для обробки () функції за допомогою цього механізму, для виконання різних частин продуктивності.
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
Але це також друга мета ключового слова прибутковості. Його можна використовувати для надсилання значень генератору.
Для уточнення, невеликий приклад:
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());
Генератор послідовностей Фібоначчі за допомогою ключового слова дохід.
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
ключове слово, ключове слово прибутковість допомагає нам призупинити та відновити виконання функції
ось короткий приклад
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
роботу
будь ласка, вкажіть, будь-які пропозиції вітаються
Залежність між викликами async 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
повертає об'єкт, що містить два значення, одне - значення, а друге - булеве. Значення може бути масивом, об'єктом тощо.
Простий приклад:
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}
Я також намагаюся зрозуміти ключове слово дохідності. Виходячи з мого теперішнього розуміння, в генераторі ключове слово "вихід" працює як контекстний комутатор процесора. Під час запуску оператора дохідності всі стани (наприклад, локальні змінні) зберігаються.
Крім цього, об'єкт прямого результату буде повернутий абоненту, наприклад {value: 0, done: false}. Абонент може використовувати цей об'єкт результату, щоб вирішити, чи потрібно знову "розбудити" генератор, викликавши next () (виклик next () - це повторити виконання).
Ще одна важлива річ - це те, що вона може встановити значення для локальної змінної. Це значення може передавати абонент 'next ()', коли 'прокидається' генератор. наприклад, it.next ('valueToPass'), як-от так: "resultValue = привести slowQuery (1);" Як і під час пробудження наступного виконання, абонент може ввести деякий запущений результат до виконання (введення його в локальну змінну). Таким чином, для цього виконання існує два види держави:
контекст, який збережено в останньому виконанні.
Введені значення за допомогою тригера виконання.
Отже, за допомогою цієї функції генератор може сортувати кілька операцій з асинхронізацією. Результат першого запиту на асинхронізацію буде передано другому, встановивши локальну змінну (resultValue у наведеному вище прикладі). Другий запит на асинхронізацію може запускати лише відповідь першого запиту на асинхронізацію. Тоді другий запит на асинхронізацію може перевірити значення локальної змінної для вирішення наступних кроків, оскільки локальна змінна є введеним значенням з відповіді першого запиту.
Труднощі асинхронних запитів:
зворотний виклик пекла
втрата контексту, якщо не передавати їх як параметри зворотного дзвінка.
вихід і генератор можуть допомогти і в обох.
Без урожайності та генератора для сортування декількох запитів на асинхронізацію потрібен вкладений зворотний зв'язок з параметрами як контекст, який не легко читати та підтримувати.
Нижче наведений приклад ланцюгових запитів асинхронізації, який працює з 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 ++++++++++++
запит1 0
+++++++++++++++++++++++++
запит2 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 ++++++++++++
запит1 0
+++++++++++++++++++++++++
запит2 1
query4 0
не забувайте дуже корисний синтаксис 'x of generator' для проходження через генератор. Наступна () функція взагалі не потрібна.
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);
}