共计 3849 个字符,预计需要花费 10 分钟才能阅读完成。
作者:麦乐
起源:恒生 LIGHT 云社区
Generator 函数根本用法
function* helloWorldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
}
var hw = helloWorldGenerator();
Generator 函数调用后生成的就是一个迭代器对象,能够通过调用迭代器的 next 办法,管制函数外部代码的执行。
hw.next()
// {value: 'hello', done: false}
hw.next()
// {value: 'world', done: false}
hw.next()
// {value: 'ending', done: true}
hw.next()
// {value: undefined, done: true}
Generator 函数遇到 yield,能够在生成器函数外部暂停代码的执行使其挂起。在可迭代对象上调用 next()办法能够使代码从暂停的地位开始持续往下执行。
先来理解一下什么是可迭代对象?
可迭代对象
要成为 可迭代 对象,一个对象必须实现 <strong>@@iterator</strong>
办法。这意味着对象(或者它原型链上的某个对象)必须有一个键为 @@iterator
的属性,可通过常量 Symbol.iterator
拜访该属性:
[Symbol.iterator
] 一个无参数的函数,其返回值为一个合乎迭代器协定的对象。
let someString = "hi";
typeof someString[Symbol.iterator];
let iterator = someString[Symbol.iterator]();
iterator + ""; //"[object String Iterator]"iterator.next(); // { value:"h", done: false}
iterator.next(); // { value: "i", done: false}
iterator.next(); // { value: undefined, done: true}
再来看一下,挂起是怎么回?有一个新的名词“协程”。
什么是协程?
协程(Coroutines)是一种比线程更加轻量级的存在,正如一个过程能够领有多个线程一样,一个线程能够领有多个协程。
协程不是被操作系统内核所治理的,而是齐全由程序所管制,也就是在用户态执行。这样带来的益处是性能大幅度的晋升,因为不会像线程切换那样耗费资源。
协程不是过程也不是线程,而是一个非凡的函数,这个函数能够在某个中央挂起,并且能够从新在挂起处外持续运行。所以说,协程与过程、线程相比并不是一个维度的概念。
Generator 函数原理
Generator 函数 是协程在 ES6 的实现,最大特点就是能够交出函数的执行权(即暂停执行)。
总结下来就是:
- 一个线程存在多个协程
- Generator 函数是协程在 ES6 的实现
- Yield 挂起协程(交给其它协程),next 唤起协程
讲到这里,应该会对 Generator 函数有一个从新的意识吧。在理论的开发中,间接应用 Generator 函数的场景并不常见,因为它只能通过手动调用 next 办法实现函数外部代码的程序执行。如果设想很好是应用它,能够为 Generator 函数实现一个主动执行神器。
主动执行的 Generator 函数
能够依据 g.next()的返回值 {value: ”, done: false} 中 done 的值让 Generator 函数递归自执行:
function run(generator) {var g = generator();
var next = function() {var obj = g.next()
console.log(obj)
if(!obj.done) {next()
}
}
next()}
run(helloWorldGenerator)
这样写能实现自执行性能,然而 不能保障执行程序。模仿两个异步申请:
function sleep1() {return new Promise((resolve) => {setTimeout(() => {console.log('sleep1')
resolve(1)
}, 1000)
})
}
function sleep2() {return new Promise((resolve) => {setTimeout(() => {console.log('sleep2')
resolve(2)
}, 1000)
})
}
批改函数
function* helloWorldGenerator() {yield sleep1();
console.log(1);
yield sleep2();
console.log(2);
}
执行 run(helloWorldGenerator)
看一下打印程序:
异步函数还是在同步代码执行完当前执行的,如果想要实现异步代码也能依照程序执行,能够对代码进一步优化:
function run(generator) {var g = generator();
var next = function() {var obj = g.next();
console.log(obj)
if(obj.done) return;
// 如果 yield 前面返回值不是 promise,能够应用 Promise.resolve 包裹一下,避免报错
Promise.resolve(obj.value).then(() => {next()})
}
next()}
如果说 sleep1 是一个网络申请的话,在 yield 前面就能够拿到申请返回的数据,看起来像是一种更优雅的异步问题解决方案。
如果想要拿到 sleep 函数 resolve 的值,也是能够实现的。
// 批改函数 变量接管 yield 语句返回后果
function* helloWorldGenerator() {var a = yield sleep1();
console.log(a);
var b = yield sleep2();
console.log(b);
}
// g.next(v); 传递后果值
function run(generator) {var g = generator();
var next = function(v) {var obj = g.next(v);
console.log(obj)
if(obj.done) return;
// 如果 yield 前面返回值不是 promise,能够应用 Promise.resolve 包裹一下,避免报错
Promise.resolve(obj.value).then((v) => {next(v)})
}
next()}
你会看到和下面一样的打印后果。
认真看下面实现形式跟 async await 很类似,实际上这就是 async await 的原理。
async await
async await 实质上就是联合 promise 实现的一个自执行 Generator 函数。将 Generator 函数的星号(*)替换成 async,将 yield 替换成 await,仅此而已
。
更具体的代码如下,感兴趣的同学能够深刻理解一下:
// async 函数就是将 Generator 函数的星号(*)替换成 async,将 yield 替换成 await,仅此而已
// async 函数返回一个 Promise 对象,能够应用 then 办法增加回调函数。当函数执行的时候,一旦遇到 await 就会先返回,等到异步操作实现,再接着执行函数体内前面的语句
// Generator 函数的执行必须靠执行器,所以才有了 co 模块,而 async 函数自带执行器和一般函数一样执行
// async 函数对 Generator 函数的改良:
/*(1)内置执行器(2)更好的语义(3)更广的适用性(4)返回值是 Promise
*/
// async 函数的实现原理,就是将 Generator 函数和主动执行器,包装在一个函数里。async function fn(args) {// ...}
// 等同于 外面的 await 换成 yield
function fn(args) {return spawn(function*() {// ...});
}
function spawn(genF) {return new Promise(function(resolve, reject) {const gen = genF();
function step(nextF) {
let next;
try {next = nextF();
} catch (e) {return reject(e);
}
if (next.done) {return resolve(next.value); // 这也是为什么 不应用 try catch 的话,异样异步申请前面的代码不再执行的起因,从这里不再持续调用 nex()办法了。}
Promise.resolve(next.value).then(function(v) {step(function() {return gen.next(v);
});
}, function(e) {step(function() {return gen.throw(e); // 这里能够解释为什么 async 函数须要应用 try catch 来捕捉异样,生成器函数的 throw,会让代码到 catch 外面
});
});
}
step(function() {return gen.next(undefined);
});
});
}
想向技术大佬们多多取经?开发中遇到的问题何处探讨?如何获取金融科技海量资源?
恒生 LIGHT 云社区,由恒生电子搭建的金融科技业余社区平台,分享实用技术干货、资源数据、金融科技行业趋势,拥抱所有金融开发者。
扫描下方小程序二维码,退出咱们!