수색…
소개
생성자 함수 ( function*
키워드로 정의)는 코 루틴으로 실행되어 반복자를 통해 요청 될 때 일련의 값을 생성합니다.
통사론
- 함수 * 이름 (매개 변수) {항복 값; 반환 값}
- 생성자 = 이름 (인수)
- {값, 완료} = generator.next (값)
- {값, 완료} = generator.return (값)
- generator.throw (오류)
비고
생성기 기능은 ES 2015 사양의 일부로 도입 된 기능으로 모든 브라우저에서 사용할 수있는 것은 아닙니다. 또한 v6.0
부터 Node.js에서 완전히 지원됩니다. 자세한 브라우저 호환성 목록은 MDN 설명서를 참조하고 Node는 node.green 웹 사이트를 참조하십시오.
생성기 함수
생성기 함수 는 선언 function*
함수 로 생성됩니다. 그것이 호출 될 때, 그 시체는 즉시 실행 되지 않습니다 . 대신 함수의 실행을 단계적으로 수행하는 데 사용할 수있는 생성기 객체를 반환합니다.
함수 본문 내부의 yield
식은 실행을 일시 중단하고 다시 시작할 수있는 지점을 정의합니다.
function* nums() {
console.log('starting'); // A
yield 1; // B
console.log('yielded 1'); // C
yield 2; // D
console.log('yielded 2'); // E
yield 3; // F
console.log('yielded 3'); // G
}
var generator = nums(); // Returns the iterator. No code in nums is executed
generator.next(); // Executes lines A,B returning { value: 1, done: false }
// console: "starting"
generator.next(); // Executes lines C,D returning { value: 2, done: false }
// console: "yielded 1"
generator.next(); // Executes lines E,F returning { value: 3, done: false }
// console: "yielded 2"
generator.next(); // Executes line G returning { value: undefined, done: true }
// console: "yielded 3"
초기 반복 종료
generator = nums();
generator.next(); // Executes lines A,B returning { value: 1, done: false }
generator.next(); // Executes lines C,D returning { value: 2, done: false }
generator.return(3); // no code is executed returns { value: 3, done: true }
// any further calls will return done = true
generator.next(); // no code executed returns { value: undefined, done: true }
생성자 함수에 오류 발생
function* nums() {
try {
yield 1; // A
yield 2; // B
yield 3; // C
} catch (e) {
console.log(e.message); // D
}
}
var generator = nums();
generator.next(); // Executes line A returning { value: 1, done: false }
generator.next(); // Executes line B returning { value: 2, done: false }
generator.throw(new Error("Error!!")); // Executes line D returning { value: undefined, done: true}
// console: "Error!!"
generator.next(); // no code executed. returns { value: undefined, done: true }
되풀이
생성기는 반복 가능 합니다. for...of
문으로 반복 할 수 있으며 반복 프로토콜에 의존하는 다른 구문에 사용됩니다.
function* range(n) {
for (let i = 0; i < n; ++i) {
yield i;
}
}
// looping
for (let n of range(10)) {
// n takes on the values 0, 1, ... 9
}
// spread operator
let nums = [...range(3)]; // [0, 1, 2]
let max = Math.max(...range(100)); // 99
다음은 ES6에서 생성자 - 사용자 정의 iterable 객체를 사용하는 또 다른 예제입니다. 여기에 익명의 생성기 함수 function *
사용되었습니다.
let user = {
name: "sam", totalReplies: 17, isBlocked: false
};
user[Symbol.iterator] = function *(){
let properties = Object.keys(this);
let count = 0;
let isDone = false;
for(let p of properties){
yield this[p];
}
};
for(let p of user){
console.log( p );
}
생성자에게 값 보내기
next()
메서드에 전달하여 값을 생성기로 보낼 수 있습니다.
function* summer() {
let sum = 0, value;
while (true) {
// receive sent value
value = yield;
if (value === null) break;
// aggregate values
sum += value;
}
return sum;
}
let generator = summer();
// proceed until the first "yield" expression, ignoring the "value" argument
generator.next();
// from this point on, the generator aggregates values until we send "null"
generator.next(1);
generator.next(10);
generator.next(100);
// close the generator and collect the result
let sum = generator.next(null).value; // 111
다른 생성기로 위임
발전기 기능 내에서, 제어는 yield*
사용하여 다른 발전기 기능으로 위임 될 수 있습니다.
function* g1() {
yield 2;
yield 3;
yield 4;
}
function* g2() {
yield 1;
yield* g1();
yield 5;
}
var it = g2();
console.log(it.next()); // 1
console.log(it.next()); // 2
console.log(it.next()); // 3
console.log(it.next()); // 4
console.log(it.next()); // 5
console.log(it.next()); // undefined
Iterator-Observer 인터페이스
생성기는 Iterator
와 Observer
의 두 가지 조합입니다.
반복자
iterator는 호출 될 때 iterable
반환하는 무언가이다. iterable
은 반복 할 수있는 항목입니다. ES6 / ES2015부터 모든 컬렉션 (Array, Map, Set, WeakMap, WeakSet)은 Iterable 계약을 따릅니다.
생성자 (반복자)는 제작자입니다. 반복에서 소비자는 생산자로부터 가치를
PULL
.
예:
function *gen() { yield 5; yield 6; }
let a = gen();
a.next()
를 호출 할 때마다 a.next()
Iterator에서 값을 pull
오고 yield
에서 실행을 pause
합니다. 다음에 a.next()
를 호출하면 이전에 일시 중지 된 상태에서 실행이 다시 시작됩니다.
관찰자
생성기는 또한 일부 값을 생성기로 다시 보낼 수있는 옵서버입니다.
function *gen() {
document.write('<br>observer:', yield 1);
}
var a = gen();
var i = a.next();
while(!i.done) {
document.write('<br>iterator:', i.value);
i = a.next(100);
}
여기에서 yield 1
은 어떤 값으로 평가되는 식처럼 사용된다는 것을 알 수 있습니다. 그것이 평가하는 값은 a.next
함수 호출에 인수로 보낸 값입니다.
그래서 처음으로 i.value
가 첫 번째 값 ( 1
)이되고, 다음 상태로 반복 할 때 a.next(100)
사용하여 값을 생성기로 다시 보냅니다.
생성기와 비동기하기
생성기는 spawn
(taskJS 또는 co) 함수와 함께 널리 사용되며 함수는 생성기를 사용하며 동기식으로 비동기 코드를 작성할 수 있습니다. 이것은 비동기 코드가 동기 코드로 변환되거나 동 기적으로 실행된다는 의미는 아닙니다. 즉, sync
처럼 보이지만 내부적으로 여전히 async
코드를 작성할 수 있음을 의미합니다.
동기화가 차단 중입니다. 비동기 대기 중입니다. 블록하는 코드를 작성하는 것은 쉽습니다. PULLING 할 때 값이 지정 위치에 나타납니다. PUSHing 할 때 콜백의 인수 위치에 값이 나타납니다.
당신이 반복자를 사용하면 PULL
생산자의 값을. 콜백을 사용하면 생성자는 콜백의 인수 위치에 값을 PUSH
합니다.
var i = a.next() // PULL
dosomething(..., v => {...}) // PUSH
여기에서 a.next()
에서 값을 가져 a.next()
두 번째에는 v => {...}
가 콜백이고 값은 콜백 함수의 인수 위치 v
로 PUSH
됩니다.
이 푸시 푸시 메커니즘을 사용하여 다음과 같은 비동기 프로그래밍을 작성할 수 있습니다.
let delay = t => new Promise(r => setTimeout(r, t));
spawn(function*() {
// wait for 100 ms and send 1
let x = yield delay(100).then(() => 1);
console.log(x); // 1
// wait for 100 ms and send 2
let y = yield delay(100).then(() => 2);
console.log(y); // 2
});
위의 코드를 보면 비 blocking
(yield 문이 100ms를 기다린 다음 실행을 계속하는 것처럼 보입니다)하지만 비동기 코드가 작성되어 있지만 실제로 waiting
있습니다. 생성기의 pause
및 resume
속성을 사용하면이 놀라운 트릭을 수행 할 수 있습니다.
어떻게 작동합니까?
스폰 함수는 yield promise
프로 시저를 사용하여 생성기에서 약속 상태를 가져오고 약속이 해결 될 때까지 기다린 다음 해결 된 값을 생성기로 푸시하여 소비 할 수 있도록합니다.
지금 사용하십시오.
따라서 Generators 및 Spawn 기능을 사용하면 NodeJS의 모든 비동기 코드를 동기식처럼 보이게 정리할 수 있습니다. 이렇게하면 디버깅이 쉬워집니다. 또한 코드가 깔끔하게 보일 것입니다.
이 기능은 async...await
as async...await
처럼 JavaScript의 차기 버전에서 제공 될 예정입니다. 그러나 라이브러리 (taskjs, co 또는 bluebird)에 정의 된 스폰 함수를 사용하여 ES2015 / ES6에서 오늘 사용할 수 있습니다
발전기가있는 비동기 흐름
생성기는 일시 중지 한 다음 실행을 재개 할 수있는 기능입니다. 이것은 외부 라이브러리, 주로 q 또는 co를 사용하여 비동기 함수를 에뮬레이션 할 수있게합니다. 기본적으로 비동기 결과를 기다리는 함수를 작성할 수 있습니다.
function someAsyncResult() {
return Promise.resolve('newValue')
}
q.spawn(function * () {
var result = yield someAsyncResult()
console.log(result) // 'newValue'
})
이렇게하면 비동기 코드를 동기식으로 작성하는 것이 가능합니다. 또한 여러 비동기 블록에 대한 작업을 시도해보십시오. 약속이 거부되면 다음 catch가 오류를 포착합니다.
function asyncError() {
return new Promise(function (resolve, reject) {
setTimeout(function () {
reject(new Error('Something went wrong'))
}, 100)
})
}
q.spawn(function * () {
try {
var result = yield asyncError()
} catch (e) {
console.error(e) // Something went wrong
}
})
co를 사용하면 q.spawn
대신 co(function * (){...})
와 q.spawn