一、Generator
异步编程的方法
- 回调函数
- 事件监听
- 发布/订阅
- promise对象
什么是异步?
当前操作A执行一半时候去,操作b事件,一段时间后在操作A事件。(存在中断操作)
Generator 函数
Generator在ES6实现,可以交出函数的执行权(让函数暂时停止)
function* gen(x){
var y = yield x + 2;
return y;
}
整个Generator 函数就是一个异步容器,在异步操作需要暂停的时候,用yield 标记
var g = gen(1);
g.next() // { value: 3, done: false }
g.next() // { value: undefined, done: true }
执行 gen函数返回一个内部指针,返回的不是一个结果,需要调用next函数,移动函数指针,让指针指向下一个阶段(第一个遇到的yield ),返回的信息是一个对象包含了当前函数执行的value值,和done标记是否执行完毕。ture代表执行完毕,false戴代表还有下一阶段
Generator可以进行数据交换和错误处理
next上一次的处理结果返回
function* gen(x){
var y = yield x + 2;
return y;
}
var g = gen(1);
g.next() // { value: 3, done: false }
g.next(2) // { value: 2, done: true }
g.next(2) 会把参数传递进去,供下一个阶段来使用。
错误处理
function* gen(x){
try {
var y = yield x + 2;
} catch (e){
console.log(e);
}
return y;
}
var g = gen(1);
g.next();
g.throw('出错了');
g.throw代表着当前阶段出现了错误,g.throw错误码被try/catch捕获
Generator 函数的用法
var fetch = require('node-fetch');
function* gen(){
var url = 'https://api.github.com/users/github';
var result = yield fetch(url);
console.log(result.bio);
}
//调用
var g = gen();
//移动指针
var result = g.next();
//返回promise结果
result.value.then(function(data){
//拿到异步的值并返回
return data.json();
}).then(function(data){
//拿到上一个阶段的数据,传递进去console 就能打印数据啦
g.next(data);
});
二、Thunk 函数的含义和用法
求值策略,即函数的参数到底应该何时求值。
求职策略分为两种,传值和传名调用,顾名思义,传值及传递一个处理好的值(如1+2,计算后传入,),传名,及把参数/函数名称传递,需要时在计算。
f(1+5) ==>f(6)
f(1+5)===>f(1+5)*1
Thunk 函数的含义
编译器的"传名调用"实现,往往是将参数放到一个临时函数之中,再将这个临时函数传入函数体。这个临时函数就叫做 Thunk 函数。
function f(m){
return m * 2;
}
f(x + 5);
// 等同于
var thunk = function () {
return x + 5;
};
function f(thunk){
return thunk() * 2;
}
也就是将x+5 替换成 function (){retrun x+5} 这个函数在执行
JavaScript 语言的 Thunk 函数
JavaScript 语言是传值调用,它的 Thunk 函数含义有所不同。在 JavaScript 语言中,Thunk 函数替换的不是表达式,而是多参数函数,将其替换成单参数的版本,且只接受回调函数作为参数。
// 正常版本的readFile(多参数版本)
fs.readFile(fileName, callback);
// Thunk版本的readFile(单参数版本)
var readFileThunk = Thunk(fileName);
readFileThunk(callback);
var Thunk = function (fileName){
return function (callback){
return fs.readFile(fileName, callback);
};
};
简单的 Thunk 函数转换器
const fs = require('fs')
var Thunk = function (fn) {
return function (){
var args = Array.prototype.slice.call(arguments)
return function (callback){
args.push(callback)
fn.apply(this.callback,args)
}
}
}
function callback(err){
console.log(err,'读取成功')
}
//生成Thunk函数
var readFileThunk = Thunk(fs.readFile);
//传入callback 执行
readFileThunk('fileA')(callback)
Thunkify 模块
使用方法
var thunkify = require('thunkify');
var fs = require('fs');
var read = thunkify(fs.readFile);
read('package.json')(function(err, str){
// ...
});
Thunkify 的源码确保了回调函数只运行一次。
Generator 函数的流程管理
封装异步操作
var fs = require('fs');
var thunkify = require('thunkify');
var readFile = thunkify(fs.readFile);
var gen = function* (){
var r1 = yield readFile('/etc/fstab');
console.log(r1.toString());
var r2 = yield readFile('/etc/shells');
console.log(r2.toString());
};
var g = gen();
var r1 = g.next();
r1.value(function(err, data){
if (err) throw err;
var r2 = g.next(data);
r2.value(function(err, data){
if (err) throw err;
g.next(data);
});
})
yield 命令用于将程序的执行权移出 Generator 函数,那么就需要一种方法,将执行权再交还给 Generator 函数。
这种方法就是 Thunk 函数,因为它可以在回调函数里,将执行权交还给 Generator 函数。
co 函数库的含义和用法
co 函数库是一个自执行的迭代器。
co函数用法
var gen = function* (){
var f1 = yield readFile('/etc/fstab');
var f2 = yield readFile('/etc/shells');
console.log(f1.toString());
console.log(f2.toString());
};
var co = require('co');
co(gen).then(function (){
console.log('Generator 函数执行完成');
})
//执行完毕输出执行完成
co函数原理
将两种自动执行器(Thunk 函数和 Promise 对象),包装成一个库。以前在操作Generator 函数就是一个异步操作的容器,需要回调函数或者promise对象才能将结果返回。
基于 Promise 对象的自动执行
var fs = require('fs');
var readFile = function (fileName){
return new Promise(function (resolve, reject){
fs.readFile(fileName, function(error, data){
if (error) reject(error);
resolve(data);
});
});
};
//Generator 函数
var gen = function* (){
var f1 = yield readFile('/etc/fstab');
var f2 = yield readFile('/etc/shells');
console.log(f1.toString());
console.log(f2.toString());
};
//手动执行
var g = gen();
g.next().value.then(function(data){
g.next(data).value.then(function(data){
g.next(data);
});
})
//自动执行
function run(gen){
var g = gen();
function next(data){
var result = g.next(data);
if (result.done) return result.value;
result.value.then(function(data){
next(data);
});
}
next();
}
run(gen);
co函数源码
function co(gen) {
var ctx = this;
var args = slice.call(arguments, 1)
return new Promise(function(resolve, reject) {
if (typeof gen === 'function') gen = gen.apply(ctx, args);
if (!gen || typeof gen.next !== 'function') return resolve(gen);
onFulfilled();
function onFulfilled(res) {
var ret;
try {
ret = gen.next(res);
} catch (e) {
return reject(e);
}
next(ret);
}
function onRejected(err) {
var ret;
try {
ret = gen.throw(err);
} catch (e) {
return reject(e);
}
next(ret);
}
function next(ret) {
if (ret.done) return resolve(ret.value);
var value = toPromise.call(ctx, ret.value);
if (value && isPromise(value)) return value.then(onFulfilled, onRejected);
return onRejected(new TypeError('You may only yield a function, promise, generator, array, or object, '
+ 'but the following object was passed: "' + String(ret.value) + '"'));
}
});
}
co调用时返回的是一个promise对象,在返回的promse对象中,首先判断gen是否个函数,如果是就执行该函数,得到一个内部指针对象;如果不是就返回,并将 Promise 对象的状态改为 resolved 。然后调用onFulfilled方法,如果出现错误会捕获该对象,没有错误直接调用next函数
,然后递归调用next函数返回结果。
async 函数的含义和用法
async 函数是什么?async 函数就是 Generator 函数的语法糖。
async 函数的实现
async 函数的实现,就是将 Generator 函数和自动执行器,包装在一个函数里。
async function fn(args){
// ...
}
// 等同于
function fn(args){
return spawn(function*() {
// ...
});
}
spawn 函数的实现
function spawn(genF) {
return new Promise(function(resolve, reject) {
var gen = genF();
function step(nextF) {
try {
var next = nextF();
} catch(e) {
return reject(e);
}
if(next.done) {
return resolve(next.value);
}
Promise.resolve(next.value).then(function(v) {
step(function() { return gen.next(v); });
}, function(e) {
step(function() { return gen.throw(e); });
});
}
step(function() { return gen.next(undefined); });
});
}
function fn(args) {
return spawn(function* () {
let a = 15;
let b = 20;
let c = yield Promise.resolve(5)
return a+b+c
});
}
let fns = fn()
fns.then((data)=>{
console.log(data)
})