generator/co
讲之前先来看一个es6的东西
复制 function* foo() {
yield 1;
yield 2;
yield 3;
return console.log(4)
}
var it = foo();
it.next();
it.next();
it.next();
it.next(); //4
复制 function *doSomething() {
console.log(‘start’)
yield
console.log(‘finish’)
}
var func1 = doSomething();
func1.next();
func1.next();
function* getStorckPrice(stock) {
while (true) {
yield Math.random() * 100;
}
}
var priceGenerator = getStockPrice(‘IBM’);
var limitPrice = 15;
var price = 100;
while (price > limitPrice) {
price = priceGenerator.next().value;
console.log( `this generator return ${price}` );
}
console.log( `buying at ${price}` );
generator 原理
我们 Google 了一下,这个是 facebook 下的一个工具:regeneratorRuntime ,用于编译 ES6 的 generator 函数。
我们进行下载
复制 npm install - g regenerator
然后创建一个generator, js文件
复制 function* hiGenerator() {
yield '1';
yield '2';
return '3';
}
执行以下代码
复制 regenerator geberator.js > generator - es5.js
以下称为编译简单版, 这个肯定是不能跑的,因为有我们遇到的 regeneratorRuntime。wap.
复制 "use strict";
var _marked =
/*#__PURE__*/
regeneratorRuntime.mark(hiGenerator);
function hiGenerator() {
return regeneratorRuntime.wrap(function hiGenerator$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
_context.next = 2;
return '1';
case 2:
_context.next = 4;
return '2';
case 4:
return _context.abrupt("return", '3');
case 5:
case "end":
return _context.stop();
}
}
}, _marked);
}
主要用到迭代器的 Iterator.next 的调用。
regenerator --include-runtime geberator.js > generator-es5.js
或者可以得出一个编译后 700 多行的点东西,我们抽离除主要逻辑
复制 var _marked =
/*#__PURE__*/
regeneratorRuntime.mark(hiGenerator);
还有
复制 function hiGenerator() {
return regeneratorRuntime.wrap(function hiGenerator$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
_context.next = 2;
return '1';
case 2:
_context.next = 4;
return '2';
case 4:
return _context.abrupt("return", '3');
case 5:
case "end":
return _context.stop();
}
}
}, _marked);
}
接下来我们看一下 mark 函数
复制 exports.mark = function(genFun) {
if (Object.setPrototypeOf) {
Object.setPrototypeOf(genFun, GeneratorFunctionPrototype);
} else {
genFun.__proto__ = GeneratorFunctionPrototype;
if (!(toStringTagSymbol in genFun)) {
genFun[toStringTagSymbol] = "GeneratorFunction";
}
}
genFun.prototype = Object.create(Gp);
return genFun;
};
里面 GeneratorFunctionPrototype 和 Gp 变量, 我们查看对应的代码:
function Generator(){}
function GeneratorFunction(){}
它们的作用就是维持关系链,跟原生的保持一致。例如:
复制 function* f() {}
var g = f();
console.log(g.__proto__ === f.prototype); // true
console.log(g.__proto__.__proto__ === f.__proto__.prototype); // true
我们将 next、throw、return 函数怪哉gp对象上。
复制 function defineIteratorMethods(prototype) {
["next", "throw", "return"].forEach(function(method) {
prototype[method] = function(arg) {
return this._invoke(method, arg);
};
});
}
复制 function wrap(innerFn, outerFn, self, tryLocsList) {
var protoGenerator = outerFn && outerFn.prototype instanceof Generator ? outerFn : Generator;
var generator = Object.create(protoGenerator.prototype);
var context = new Context(tryLocsList || []);
generator._invoke = makeInvokeMethod(innerFn, self, context);
return generator;
}
所以当我们 hell = hllGenerator() 的时候,执行的其实是 wrap 函数,wrap 函数返回一个 generator, generator 对象,她有 outerFn、outerFn. 其实就是genFun.prototype, getFun.prototype 是一个控对象,原型上面有next()方法。hell.next() 的时候,其实是 generator._invoke = makeInvokeMethod(innerFn, self, context); 这里的 innerFun 就是wrap 包裹的函数。hiGenerator接下来我们看一下 makeInvokeMethod 函数里面的主要功能是 invoke 函数 根据不同的context状态来进行相应的操作。hi.next()=>record=> innerFn.call(self, context)=>abrupt,我们 record 对象。这个对象有 complete 方法。
语法上generator,有两个特征:
函数体内部使用yield关键字,定义不同的内部状态。
generator 本意是 iterator 生成器,函数运行到yield时退出,并保留上下文。控制函数的执行过程,手工暂停和恢复代码执行. generator 的弊端是没有执行器,本身也不是为流程控制而生的,所以出现了co co 是 tj 大神开发的库 github地址
复制 const co = require('co')
const Promise = require('bluebird')
const fs = Promise.promisifyAll(require('fs'))
async function main(){
const contents = co(function*{
var result = yield.fs.readFilAsyn('myfiles.js', 'utf8')
return result;
})
}
async/await
async 函数的执行过程
1)await帮我们处理了 promise,要么返回兑现的值,要么抛出异常;
2)await在等待 promise 兑现的同时,整个 async 函数会挂起,promise 兑现后再重新执行接下来的代码。
复制 const readAsync = util.promisify(fs.readFile)
async function init() {
let data = await readAsync('./package-lock.json')
data = JSON.parse(data)
console.log(data.name)
}
init()
async/await 原理
我们用 babel 编译下,async/await转换出什么。
复制 //源代码
function tell() {
return new Promise((reslove, reject) => {
if (true) {
reslove(1)
} else {
reject(2)
}
})
}
var a = (
async () => {
await tell().then((res) => {
console.log('res', res);
}).catch(err => {
console.log('err: ', err);
})
}
)
console.log('a', a())
//转换后的
"use strict";
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
try {
var info = gen[key](arg);
var value = info.value;
} catch (error) {
reject(error);
return;
}
if (info.done) {
resolve(value);
} else {
Promise.resolve(value).then(_next, _throw);
}
}
function _asyncToGenerator(fn) {
return function() {
var self = this,
args = arguments;
return new Promise(function(resolve, reject) {
var gen = fn.apply(self, args);
function _next(value) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
}
function _throw(err) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
}
_next(undefined);
});
};
}
function tell() {
return new Promise(function(reslove, reject) {
if (true) {
reslove(1);
} else {
reject(2);
}
});
}
var a =
/*#__PURE__*/
function() {
var _ref = _asyncToGenerator(
/*#__PURE__*/
regeneratorRuntime.mark(function _callee() {
return regeneratorRuntime.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
_context.next = 2;
return tell().then(function(res) {
console.log('res', res);
})["catch"](function(err) {
console.log('err: ', err);
});
case 2:
case "end":
return _context.stop();
}
}
}, _callee);
}));
return function a() {
return _ref.apply(this, arguments);
};
}();
console.log('a', a());
yeah,瞬间好长一串,不过莫慌。我们先理性(慌张)分析一波。 它先定义了 三个个函数 asyncGeneratorStep、 _asyncToGenerator、tell wow, 我们发现就tell这个函数我们看的懂,而且很熟悉, Promsie(不熟悉的先去补补上面的知识),ok, 在看剩下来的两个函数。我们大概分析下,asyncGeneratorStep 这个应该是根据不同的状态我们要怎么处理(做具体的事情)。 _asyncToGenerator、tell 这个函数呢,则是将传入的函数变量做分配处理,起分配作用。(不做具体事情)。
接来下我们看看 a定义的这个主函数。整个采用了switch模式,根据不同的状态,分发事件。
根据以上原理 我们可以写一个简易版的
复制 var myAsync = generator => {
// 注意 iterator.next() 返回对象的 value 是 promiseAjax(),一个 promise
const iterator = generator();
// handle 函数控制 async 函数的 挂起-执行
const handle = iteratorResult => {
if (iteratorResult.done) return;
const iteratorValue = iteratorResult.value;
// 只考虑异步请求返回值是 promise 的情况
if (iteratorValue instanceof Promise) {
// 递归调用 handle,promise 兑现后再调用 iterator.next() 使生成器继续执行
iteratorValue
.then(result => handle(iterator.next(result)))
.catch(e => iterator.throw(e));
}
};
try {
handle(iterator.next());
} catch (e) {
console.log(e);
}
};
myAsync(function*() {
try {
const a = yield Promise.resolve(1);
const b = yield Promise.reject(a + 10);
const c = yield Promise.resolve(b + 100);
console.log(a, b, c); // 输出 1,11,111
} catch (e) {
console.log("出错了:", e);
}
});
generator()生成它的控制器。接着调用 handle 函数,并传入 interator.next(), 接着遇到 yield 生成器再次挂起,把结果(promsie)传给 handle,接着在执行 interatorValue.then(),将异步请求的值传给 interator.next(), 再次执行生成器。最后当(iteratorResult.done)为true时,退出。
await 的用法 大概分为三种:
复制 //这是await 和 promsie 结合
function get1(){
return 1
}
function get2(){
return 2
}
function get3(){
return 3
}
function get4(){
return 4
}
(async function async(){
// try{
// } catch(err){
// console.log('err: ', err);
// }
const g1 = get1();
const g2 = get2();
const g3 = get3();
const g4 = get4();
let sum =0;
let data = await Promise.all([g1,g2,g3,g4])
for(v of data){
sum = sum+v;
}
console.log(sum)
})()
后记
相关知识参考资料
Promises/A+规范-英文
Promises/A+规范-翻译1
Promises/A+规范-翻译-推荐
JS执行栈
Javascript异步编程的4种方法