面接官が満足するGeneratorアクチュエータをどう書きますか?
12797 ワード
今はasync関数を使ってGeneratorアクチュエータに代わることができますが、Generatorアクチュエータの原理を知る必要があります.
Generatorを知らないなら、ここを見てください.
例はすべてConsoneで実行できる(Googleバージョン76.0.3809.100).最新のブラウザでサポートされているJavaScript特性で作成されており、互換性は考慮されない.
用語の区分
GeneratorはGenerator functionを実行して戻ってくる対象です.
アクチュエータの原理
原理1
Generator next関数の特性があり、next関数が実行されると次のような構造に戻ります.
原理二
yield表現自体は戻り値がない、あるいは常にundefinedに戻ります.
nextメソッドは前のyield表現の戻り値としてパラメータを持つことができます.
nextメソッドのパラメータは前のyield表現の戻り値を表していますので、nextメソッドを初めて使用した場合、伝達パラメータは無効です.
ただし、注意が必要です.
したがって、すべての
簡単なアクチュエータ
簡単なアクチュエータコードは以下の通りです.
yieldを考えるタイプはPromiseです.
上のコードが以下のGenerator関数を実行するのは正しくないです.
私達のコードはこのように処理する必要があります.
try catchキャプチャについて考えるyieldエラー
ここは上に述べた原理3を使っています.よく分からないなら帰ってみてもいいです.
上のアクチュエータを使ってはtry catchからyieldまでのエラーはできません.
このように変えなければなりません.
yieldの他のタイプを考慮して、例えばgenerator関数はこれらのタイプをPromiseに適合させることができます.
上のコードが以下のGenerator関数を実行するのは正しくないです.
最終版
Generatorアクチュエータは思ったほど難しくないです.時間をかけて十分食べられます.
Generatorを知らないなら、ここを見てください.
例はすべてConsoneで実行できる(Googleバージョン76.0.3809.100).最新のブラウザでサポートされているJavaScript特性で作成されており、互換性は考慮されない.
用語の区分
GeneratorはGenerator functionを実行して戻ってくる対象です.
アクチュエータの原理
原理1
Generator next関数の特性があり、next関数が実行されると次のような構造に戻ります.
{ value: 'world', done: false }
または{ value: undefined, done: true }
再帰的にnext関数を実行できます.もしdoneがtrueであれば、next関数の運転を停止します.原理二
yield表現自体は戻り値がない、あるいは常にundefinedに戻ります.
nextメソッドは前のyield表現の戻り値としてパラメータを持つことができます.
nextメソッドのパラメータは前のyield表現の戻り値を表していますので、nextメソッドを初めて使用した場合、伝達パラメータは無効です.
function* test() {
const a = yield "a"
console.log(a)
return true
}
const gen = test()
// next() yield a ,next {value: "a", done: false}
// next() ,
const nextOne = gen.next()
// nextOne.value
// test console.log 'a'
// next() {value: true, done: true}
gen.next(nextOne.value)
原理三gen.throw(exception)
は、異常を投げ、生成器の実行を再開し、doneおよびvalueの2つの属性を有するオブジェクトを返すことができる.また、この異常は、一般にtry...catch
ブロックによって捕捉され得る.私達はPromiseのミスを捨てれば、try catch
を使ってPromiseのミスをブロックすることができます.ただし、注意が必要です.
gen.throw()
は、同期した環境でコード運転を直接終了し、try catchができないことを知ることが特に重要である.しかし、gen.throw()
は、非同期Promise環境においては、リターン値があると同時に、generator関数のtry catch yield文であれば、gen.throw
からのエラーをキャプチャすることができる.したがって、すべての
gen.throw()
を一つのPromise実行環境に包装してこそ、有効になることを確認する必要があります.簡単なアクチュエータ
簡単なアクチュエータコードは以下の通りです.
/**
* generator
* @param {Function} func generator
* @return {Promise} Promise
*/
function generatorExecuter(func) {
const gen = func()
function recursion(prevValue) {
const next = gen.next(prevValue)
const value = next.value
const done = next.done
if (done) {
return Promise.resolve(value)
} else {
return recursion(value)
}
}
return recursion()
}
コードは複雑ではありません.一番簡単なアクチュエータが出ます.もしあなたが一歩ずつ文章を読んできたなら、原理を理解しています.コードもよく分かります.yieldを考えるタイプはPromiseです.
上のコードが以下のGenerator関数を実行するのは正しくないです.
function* test() {
const a = yield Promise.resolve('a')
console.log(a)
return true
}
generatorExecuter(test)
上記のコードを実行した後、test関数consolie.logはa
ではなく、Promise {: "a"}
を出力します.私達のコードはこのように処理する必要があります.
/**
* generator
* @param {GeneratorFunction} generatorFunc generator
* @return {Promise} Promise
*/
function generatorExecuter(generatorFunc) {
return new Promise((resolve) => {
const generator = generatorFunc()
// next
onFullfilled()
/**
* Promise
* generator.next()
* @param {Any} value yield
*/
function onFullfilled(value) {
let result
// next() generator ,
result = generator.next(value)
next(result)
}
function next(result) {
const value = result.value
const done = result.done
if (done) {
return resolve(value)
} else {
return Promise.resolve(value).then(onFullfilled)
}
}
})
}
これでgenerantorExecuterを再稼働すれば大丈夫です.try catchキャプチャについて考えるyieldエラー
ここは上に述べた原理3を使っています.よく分からないなら帰ってみてもいいです.
上のアクチュエータを使ってはtry catchからyieldまでのエラーはできません.
function* test() {
try {
const a = yield Promise.reject('error')
}catch (err) {
console.log(' :', err)
}
return true
}
generatorExecuter(test)
上記のコードを実行すると、ブロック後にUncaught (in promise) error
を出力するのではなく、 : error
にエラーが発生します.このように変えなければなりません.
/**
* generator
* @param {GeneratorFunction} generatorFunc generator
* @return {Promise} Promise
*/
function generatorExecuter(generatorFunc) {
return new Promise((resolve, reject) => {
const generator = generatorFunc()
// next
onFullfilled()
/**
* Promise
* generator.next()
* @param {Any} value yield
*/
function onFullfilled(value) {
let result
// next() generator ,
// , , reject
try {
// yield , undefined。
// generator.next , yield 。
// generator.next yield ,
// generator.next , 。
result = generator.next(value)
} catch (error) {
return reject(error)
}
next(result)
}
/**
* Promise
* generator.throw() , try catch yield xxx
* @param {Any} reason
*/
function onRejected(reason) {
let result
try {
// gen.throw() , ,
// done value 。
// gen.throw() , try catch,
// gen.throw() Promise ,
// generator try catch yield gen.throw
result = generator.throw(reason)
} catch (error) {
return reject(error)
}
// gen.throw() ,
next(result)
}
function next(result) {
const value = result.value
const done = result.done
if (done) {
return resolve(value)
} else {
return Promise.resolve(value).then(onFullfilled, onRejected)
}
}
})
}
yieldの他のタイプを考えるyieldの他のタイプを考慮して、例えばgenerator関数はこれらのタイプをPromiseに適合させることができます.
上のコードが以下のGenerator関数を実行するのは正しくないです.
function* aFun() {
return 'a'
}
function* test() {
const a = yield aFun
console.log(a)
return true
}
generatorExecuter(test)
上のコードを実行した後、test関数consolie.logはa
ではなく、下の文字列を出力します.ƒ* aFun() {
return 'a'
}
私達のコードはこのように処理する必要があります./**
* generator
* @param {GeneratorFunction | Generator} generatorFunc Generator Generator
* @return {Promise} Promise
*/
function generatorExecuter(generatorFunc) {
if (!isGernerator(generatorFunc) && !isGerneratorFunction(generatorFunc)) {
throw new TypeError(
'Expected the generatorFunc to be a GeneratorFunction or a Generator.'
)
}
let generator = generatorFunc
if (isGerneratorFunction(generatorFunc)) {
generator = generatorFunc()
}
return new Promise((resolve, reject) => {
// next
onFullfilled()
/**
* Promise
* generator.next()
* @param {Any} value yield
*/
function onFullfilled(value) {
let result
// next() generator ,
// , , reject
try {
// yield , undefined。
// generator.next , yield 。
// generator.next yield ,
// generator.next , 。
result = generator.next(value)
} catch (error) {
return reject(error)
}
next(result)
}
/**
* Promise
* generator.throw() , try catch yield xxx
* @param {Any} reason
*/
function onRejected(reason) {
let result
try {
// gen.throw() , ,
// done value 。
// gen.throw() , try catch,
// gen.throw() Promise ,
// generator try catch yield gen.throw
result = generator.throw(reason)
} catch (error) {
return reject(error)
}
// gen.throw() ,
next(result)
}
function next(result) {
const value = toPromise(result.value)
const done = result.done
if (done) {
return resolve(value)
} else {
return value.then(onFullfilled, onRejected)
}
}
})
}
/**
* yield , generator
* Promise
*/
function toPromise(value) {
if (isGerneratorFunction(value) || isGernerator(value)) {
// generatorExecuter Promise
return generatorExecuter(value)
} else {
// 、 、 、Promise Promise
return Promise.resolve(value)
}
}
/**
* generator
*/
function isGerneratorFunction(target) {
if (
Object.prototype.toString.apply(target) === '[object GeneratorFunction]'
) {
return true
} else {
return false
}
}
/**
* generator
*/
function isGernerator(target) {
if (Object.prototype.toString.apply(target) === '[object Generator]') {
return true
} else {
return false
}
}
これでgenerantorExecuterを再稼働すれば大丈夫です.最終版
Generatorアクチュエータは思ったほど難しくないです.時間をかけて十分食べられます.
/**
* generator
* @param {GeneratorFunction | Generator} generatorFunc Generator Generator
* @return {Promise} Promise
*/
function generatorExecuter(generatorFunc) {
if (!isGernerator(generatorFunc) && !isGerneratorFunction(generatorFunc)) {
throw new TypeError(
'Expected the generatorFunc to be a GeneratorFunction or a Generator.'
)
}
let generator = generatorFunc
if (isGerneratorFunction(generatorFunc)) {
generator = generatorFunc()
}
return new Promise((resolve, reject) => {
// next
onFullfilled()
/**
* Promise
* generator.next()
* @param {Any} value yield
*/
function onFullfilled(value) {
let result
// next() generator ,
// , , reject
try {
// yield , undefined。
// generator.next , yield 。
// generator.next yield ,
// generator.next , 。
result = generator.next(value)
} catch (error) {
return reject(error)
}
next(result)
}
/**
* Promise
* generator.throw() , try catch yield xxx
* @param {Any} reason
*/
function onRejected(reason) {
let result
try {
// gen.throw() , ,
// done value 。
// gen.throw() , try catch,
// gen.throw() Promise ,
// generator try catch yield gen.throw
result = generator.throw(reason)
} catch (error) {
return reject(error)
}
// gen.throw() ,
next(result)
}
function next(result) {
const value = toPromise(result.value)
const done = result.done
if (done) {
return resolve(value)
} else {
return value.then(onFullfilled, onRejected)
}
}
})
}
/**
* yield , generator
* Promise
*/
function toPromise(value) {
if (isGerneratorFunction(value) || isGernerator(value)) {
// generatorExecuter Promise
return generatorExecuter(value)
} else {
// 、 、 、Promise Promise
return Promise.resolve(value)
}
}
/**
* generator
*/
function isGerneratorFunction(target) {
if (
Object.prototype.toString.apply(target) === '[object GeneratorFunction]'
) {
return true
} else {
return false
}
}
/**
* generator
*/
function isGernerator(target) {
if (Object.prototype.toString.apply(target) === '[object Generator]') {
return true
} else {
return false
}
}
運行例は以下の通りです.直接にGoogleのコンサートで実行すればいいです.//
function* one() {
return 'one'
}
function* two() {
return yield 'two'
}
function* three() {
return Promise.resolve('three')
}
function* four() {
return yield Promise.resolve('four')
}
function* five() {
const a = yield new Promise(resolve => {
setTimeout(() => {
resolve('waiting five over')
}, 1000)
})
console.log(a)
return 'five'
}
function* err() {
const a = 2
a = 3
return a
}
function* all() {
const a = yield one()
console.log(a)
const b = yield two()
console.log(b)
const c = yield three()
console.log(c)
const d = yield four()
console.log(d)
const e = yield five()
console.log(e)
try {
yield err()
} catch (err) {
console.log(' ', err)
}
return 'over'
}
generatorExecuter(all).then(value => {
console.log(value)
})
//
// generatorExecuter(all()).then(value => {
// console.log(value)
// })