原文地址:regeneratorgit
搜了一圈,關於 Generator
基本都是在講用法,但不多說起到其工做原理,也就是「協程」。但又由於這東西咱們直接或間接的天天都在使用,因而準備專門寫一篇文章來說講這個github
Callback
Deferred
也在各大開源庫能看到蹤跡,如 qiankun{ value: any, done: boolean }
來判斷狀態asyncGeneratorStep
和 _asyncToGenerator
,其實它就是自動執行功能async function a() {} function* b() {} // babel 編譯後 function asyncGeneratorStep(gen, resolve, reject, _next, ...) { // 調用 gen 的 next 或 throw 方法 var info = gen[key](arg); var value = info.value; if (info.done) { resolve(value); } else { // 遞歸執行 Promise.resolve(value).then(_next, _throw); } } function _asyncToGenerator(fn) { return function () { return new Promise(function (resolve, reject) { // 獲取 generator 對象 var gen = fn.apply(self, arguments); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } // 初始化執行 next _next(undefined); }); }; }
function* gen() {} const gObj = gen(); gObj.next(); gObj.return();
function*
語法來定義,它是 GeneratorFunction 的實例Object.getPrototypeOf(gen).constructor // GeneratorFunction {prototype: Generator, ...}
const GeneratorFunction = Object.getPrototypeOf(gen).constructor;
Function
是一個級別的,能夠傳參來建立函數,如const gen = new GeneratorFunction('a', 'yield a * 2'); const gObj = gen(10); gObj.next().value // 20
正片開始,代碼示例:api
let num = 0; async function gen() { num = num + (await wait(10)); await 123; await foo(); return num; } function wait(num: number) { return new Promise((resolve) => setTimeout(() => resolve(num), 600)); } async function foo() { await "literal"; } await gen(); console.log("regenerator: res", num);
若是你對原型鏈和繼承有所遺忘的話,建議先看下這篇 prototype&extendspromise
class GeneratorFunction {} // GeneratorFunction 的 prototype 很通用,單獨拎出來 class GeneratorFunctionPrototype { static [Symbol.toStringTag] = "GeneratorFunction"; // 實現 iterator protocol next(args) {} return(args) {} throw(args) {} // 實現 iterable protocol [Symbol.iterator]() { return this; } } // 相互引用 GeneratorFunctionPrototype.constructor = GeneratorFunction; GeneratorFunction.prototype = GeneratorFunctionPrototype; // 做用不大,設置 prototype 便可 class Generator {} Generator.prototype = GeneratorFunctionPrototype.prototype;
switch case + context 記錄參數
實現function _callee$(_context) { while (1) { switch (_context.next) { case 0: // await wait(10) _context.next = 3; return wait(10); case 3: // await 123 _context.next = 7; return 123; case 7: _context.next = 9; // await foo() return foo(); case "end": return _context.stop(); } } }
_asyncToGenerator
咱們以前說了,就是自動執行,這其實和 co(markFn)
無異。另外一方面你能夠推斷出 regeneratorRuntime.mark
函數返回的其實就是 polyfill 的 Generatorfunction _foo() { _foo = _asyncToGenerator( regeneratorRuntime.mark(function _callee2() { return regeneratorRuntime.wrap(function _callee2$(_context2) { switch (_context2.next) { case 0: _context2.next = 2; return "literal"; case "end": return _context2.stop(); } }, _callee2); }) ); return _foo.apply(this, arguments); }
{ value: "literal", done: true }
做爲了 mark 函數的返回值,並交給 _asyncToGenerator 使用,它如何使用的呢,固然是 promise.then(next)
{ value: fooRetValue, done: false }
,繼續 nextfunction mark(genFn: () => void) { return _inheritsLoose(genFn, GeneratorFunctionPrototype); } function _inheritsLoose(subClass, superClass) { Object.setPrototypeOf(subClass, superClass); subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; return subClass; }
type GeneratorMethod = "next" | "return" | "throw"; class GeneratorFunctionPrototype { // set by wrap fn private _invoke: (method: GeneratorMethod, args) => { value: any, done: boolean }; // 注意這是原型方法哦 next(args) { return this._invoke("next", args); } return(args) { return this._invoke("return", args); } throw(args) { return this._invoke("throw", args); } }
function wrap(serviceFn) { // 依然借用 GeneratorFunctionPrototype 的能力 const generator = new Generator(); const context = new Context(); let state = GenStateSuspendedStart; // 實現 _invoke generator._invoke = function invoke(method: GeneratorMethod, args) { context.method = method; context.args = args; if (method === "next") { // 記錄上下文參數 context.sent = args; } else if (method === "throw") { throw args } else { context.abrupt("return", args); } // 執行業務上的代碼 const value = serviceFn(context); state = context.done ? GenStateCompleted : GenStateSuspendedYield; return { value, done: context.done }; }; return generator; }
class Context { next: number | string = 0; sent: any = undefined; method: GeneratorMethod = "next"; args: any = undefined; done: boolean = false; value: any = undefined; stop() { this.done = true; return this.value; } abrupt(type: "return" | "throw", args) { if (type === "return") { this.value = args; this.method = "return"; this.next = "end"; } else if (type === "throw") { throw args; } } }
最後一點,可能各位用得少,但缺了的話,Generator 是不完整的babel
async function a() { const res = await b(); } async function b() { await 1; await 'str'; return { data: 'lawler', msg: 'ok' }; }
function gen$(_context) { switch (_context.next) { case 0: _context.next = 7; return wait(10); case 7: // 傳遞 foo generator object 給 gen 的 context return _context.delegateYield(foo(), "t2", 8); case "end": return _context.stop(); } }
generator._invoke = function invoke(method, args) { context.method = method; // yield* genFn 時使用,循環返回 genFn 迭代的結果,直到 return while (true) { const delegate = context.delegate; if (delegate) { const delegateResult = maybeInvokeDelegate(delegate, context); if (delegateResult) { if (delegateResult === empty) continue; // 傳出內部迭代結果 { value, done } return delegateResult; } } } if (method === "next") {} }
regenerator
作的事情還不少,如 throw error、yield* gen() 時各類情況的處理以及其餘方便的 api,喜歡的自行 dive in 吧