Promise,Generator,Await/Async
上节中忘记讲:Iterator接口和Generator函数的关系了,Symbol.iterator方法的最简单的实现就是通过Generator函数:
let myIterable = {
[Symbol.iterator]:function* (){
yield 1;
yield 2;
yield 3;
}
}
let aa = [...myIterable];
aa //[1, 2, 3]
用Generator函数写Symbol.iterator方法几乎不用部署任何代码,只要yield命令给出每一步的返回值即可。接下来开始写Generator函数吧,貌似没有promise好用,看下图,Await/Async函数是最腻害的,所以掌握最强的方法,你就可以所向披靡了。
在浏览知乎的中一个有关node.js是用来做什么的时候,看到有关ES6的新特性,然后就写个博客吧!
Part One:promise
Part Two:Generator
Generator函数是个状态机,封装多个内部状态。执行Generator函数会返回一个遍历器对象。
function* hello(){
yield 'hello';
yield 'world';
return 'ending';
} var hw = hello();
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函数,变量hw是一个指向hello函数内部状态的指针对象。函数的调用必须调用遍历器对象的next方法,使得指正移向下一个状态。调用next方法时候,内部指针从函数头部或者上一次停下来的地方开始执行。Generator函数是分段执行的,yield表达式是暂停执行的标记,next是恢复执行。
yield表达式:
在Generator函数内部,yield表达式只是暂停的标识。
yield和return的相似之处在于都能返回紧跟在语句后面的表达式的值,区别在于每次遇到yeild,函数暂停执行,下一次再从该位置继续向后执行。return不具备位置记忆的功能。Generator函数可以返回一系列的值,因为内部可以有多个yield。(Generator在英文中是“生成器”的意思)
如果Generator内部不用yield,就变成了一个单纯的暂缓执行的函数。
yield只能用在Generator函数内部,其他地方是会报错的!!!
在用for/of进行遍历Generator时候,不需要再调用next方法,例如最上面的例子:Generator和Iterator接口的关系时,举个栗子:
function* foo(){
yield 1;
yield 2;
yield 3;
return 4;
}
for(var i of foo()){
console.log(i)
}
//1 2 3
一旦next方法的返回对象的done属性为true时,for/of循环就会中止,且不包含返回值,所以上面的return语句返回的4不包含在循环中。
这里使用Generator函数写了一个斐波那契数列的方法,遍历时候将小于1000的数字打印出来:
function* fibonacci(){
let [prev,curr] = [0,1];
for(;;){
[prev,curr] = [curr,prev+curr];
yield curr;
}
}
for(let n of fibonacci()){
if(n>1000) break;
console.log(n);
}
如何遍历一个没有Iterator接口的对象呢?上一节中给对象加上Symbol.iterator属性就可以,而在Generator当中呢?
function* objectEntires(obj){
let propKeys = Reflect.ownKeys(obj); for(let propKey of propKeys){
yield [propKey,obj[propKey]];
}
} let jane = {first: 'Jane',last: 'Ostin'};
for(let [key,value] of objectEntires(jane)){
console.log(`${key}->${value}`)
}
VM4211:11 first->Jane
VM4211:11 last->Ostin 或者
函数当中涉及到Reflect,有时间写一篇有关Reflect的博文~~~
拓展运算符(...),解构赋值和Array.from()等都是遍历器接口,可以将Generator函数作为参数。
function* numbers(){
yield 1;
yield 2;
return 3;
yield 4;
}
let aa = [...numbers()]
let bb = Array.from(numbers());
let [x,y] = numbers(); aa //[1, 2]
bb //[1, 2]
x //
y //
这里有个需要特别注意的点:一旦Generator执行过程中抛出错误,且没有被内部捕获,就不会再执行下去了,如果还调用next方法,value就是undefined,done就是true了。即JavaScript引擎认为这个Generator已经运行结束了。
注意:在Generator内部调用一个Generator函数是不会起作用的,所以就需要用到yield*表达式,用于在一个Generator函数 里面执行另外一个Generator函数。
function* foo(){
yield 'a';
yield 'b';
} function* bar(){
yield 'x';
yield* foo();
yield 'y';
}
[...bar()] //["x", "a", "b", "y"]
如果不用yield* 而使用yield后面跟一个Generator函数的话,返回的就是一个遍历器对象了。在使用yield*时候(Generator里面没有return语句),就类似于for/of的简写形式。在有return语句时候呢?
任何数据结构只要有Iterator接口,就可以被yield*遍历。
let read = (function* (){
yield 'hello';
yield* 'hello';
}())
[...read] //["hello", "h", "e", "l", "l", "o"]
function* AA(){
yield 1;
return 2;
}
function* BB(){
yield 3;
let re = yield* AA();
console.log(re)
yield 4;
}
[...BB()]
//
// [3, 1, 4]
在运行内部有return的Generator函数时候,如果将它赋值为一个变量指针,则打印结果是会有return的结果输出。
出个题:如果遍历二维数组?
const tree = ['a',['b','c'],['d','e']];
function* iterTree(arr){
for(let item of arr){
if(Array.isArray(item)){
yield* iterTree(item);
}else{
yield item;
}
}
}
[...iterTree(tree)] // ["a", "b", "c", "d", "e"]
如果对象的属性是Generator函数,可以进行简写:
let obj = {
* myMethod(){
yield 1;
yield 2;
}
}
[...obj.myMethod()] //[1, 2] 类似于==》
let obj = {
myMethod:function* (){
yield 1;
yield 2;
}
}
[...obj.myMethod()] //[1, 2]
注意:Generator函数和普通构造函数的区别?
相同点:实例化后的Generator函数对象,会继承Generator函数的原型上的方法,这点跟构造函数蕾西。
区别:Generator函数返回的是遍历器对象,而不是this对象,所以函数上的固有方法,实例是不继承的,也不能new一个Generator对象,会报错。
如何将Generator函数返回一个正常的实例对象,既可以使用next方法,也可以获得正常的this对象?例如:F是一个Generator函数,可以将F内部的this对象绑定obj对象,然后调用next方法,返回Iterator对象,给obj上面添加属性,将所有内部的属性都绑定在obj对象上面,因此obj对象也就是F的实例了。
如何将obj和f统一?
可以将F的this指向它自己的原型对象。如下:
function* F(){
this.a = 1;
yield this.b = 2;
yield this.c = 3;
} var f = F.call(F.prototype);
f.next();
f.next();
f.next();
console.log(f.a,f.b,f.c); //1,2,3
就是将Generator函数里面的this指向了它本身的原型对象上面,在调用了next方法之后给原型上面添加属性。
如何让f是可以用构造函数new出来的对象,还可以使用Generator函数的next方法?
function* F(){
this.a = 1;
yield this.b = 2;
yield this.c = 3;
} function gen(){
return F.call(F.prototype);
}
var f = new gen();
f.next();
f.next();
f.next();
console.log(f.a,f.b,f.c);
VM910:14 1 2 3
就是真正的再新建一个构造函数,里面return F的实例,然后就可以new一个对象并使用Generator的方法,还有原型上面的属性了。
用Generator函数 实现一个状态机就更加简洁化,而且比较安全(状态不会给非法篡改),更加符合函数式编程的思想。
var clock = function* (){
while(true){
console.log("Tick!");
yield;
console.log("Tock!")
yield;
}
}
var c = clock();
c.next() //Tick!
c.next() //Tock!
c.next() //Tick!
Generator可以暂停函数执行,返回任意表达式的值,应用场景:
1,处理异步操作,改写回调函数
2,可以通过Generator函数逐步读取文本文件
3,控制流管理,多部操作非常耗时,采用回调函数的方式,函数嵌套的模式。如果是promise的话,可能是回调在then里面的嵌套,如果用Generator函数的话:
function scheduler(task){
var taskObj = task.next(task.value);
if(!taskObj.done){
task.value = taskObj.value;
scheduler(task);
}
}
function* longRunningTask(v1){
try{
var v2 = yield step1(v1);
var v3 = yield step2(v2);
var v4 = yield step3(v3);
var v5 = yield step4(v4);
}catch(e){
console.log(`ERR${e}`)
}
} scheduler(longRunningTask(initialValule))
那么异步的处理方法呢??下一节讲Await/Async方法,本节的Generator讲太多,篇幅过长~~~
Promise,Generator,Await/Async的更多相关文章
- javascript异步编程的前世今生,从onclick到await/async
javascript与异步编程 为了避免资源管理等复杂性的问题, javascript被设计为单线程的语言,即使有了html5 worker,也不能直接访问dom. javascript 设计之初是为 ...
- 5分种让你了解javascript异步编程的前世今生,从onclick到await/async
javascript与异步编程 为了避免资源管理等复杂性的问题,javascript被设计为单线程的语言,即使有了html5 worker,也不能直接访问dom. javascript 设计之初是 ...
- 异步解决方案----Promise与Await
前言 异步编程模式在前端开发过程中,显得越来越重要.从最开始的XHR到封装后的Ajax都在试图解决异步编程过程中的问题.随着ES6新标准的到来,处理异步数据流又有了新的方案.我们都知道,在传统的aja ...
- Promise, Generator, async/await的渐进理解
作为前端开发者的伙伴们,肯定对Promise,Generator,async/await非常熟悉不过了.Promise绝对是烂记于心,而async/await却让使大伙们感觉到爽(原来异步可以这么简单 ...
- JS异步编程 (2) - Promise、Generator、async/await
JS异步编程 (2) - Promise.Generator.async/await 上篇文章我们讲了下JS异步编程的相关知识,比如什么是异步,为什么要使用异步编程以及在浏览器中JS如何实现异步的.最 ...
- Promise、Generator,Async/await
我们知道JavaScript是单线程语言,如果没有异步编程非得卡死. 以前,异步编程的方法有下面四种 回调函数 事件监听 发布/订阅 Promise对象 现在据说异步编程终极解决方案是——async/ ...
- ES Next & Arrow function & Promise & Iterator & Generator yield & Async Await
ES Next & Arrow function & Promise & Iterator & Generator yield & Async Await co ...
- Generator与async/await与Generator的模拟
Generator 函数有多种理解角度.语法上,首先可以把它理解成,Generator 函数是一个状态机,封装了多个内部状态. 执行 Generator 函数会返回一个遍历器对象,也就是说,Gener ...
- js中异步方案比较完整版(callback,promise,generator,async)
JS 异步已经告一段落了,这里来一波小总结 1. 回调函数(callback) setTimeout(() => { // callback 函数体 }, 1000) 缺点:回调地狱,不能用 t ...
随机推荐
- uname|mv|tar -xzvf|
$ ls CAFE-4.2.1.tar.gz mcl-latest.tar.gz mysql-5.4.3-beta-linux-i686-glibc23.tar.gz.1 orthomclSoftwa ...
- 牛客-小y的盒子
题目传送门 -------------------稍加观察就会发现,4n - 1就是题目要的答案.至于为什么,看官方的题解.不过这个n非常的大,用正常快速幂解决不了.这道题我学到的就是解决幂非常大的情 ...
- 吴裕雄--天生自然python学习笔记:Python uWSGI 安装配置
本文主要介绍如何部署简单的 WSGI 应用和常见的 Web 框架. 以 Ubuntu/Debian 为例,先安装依赖包: apt-get install build-essential python- ...
- 查看python版本和django版本
python --version 在python shell中: import sys sys.version import django django.VERSION
- WEB前端资源集(二)
在上一篇为大家整理出了一些资源网站,接下来给大家整理了一些开发中常用的工具. 开发工具篇 开发工具集 Sublime Text 3:SublimeText 3是一个代码编辑器,也是HTML和散文先进的 ...
- 转:zabbix 2.0.6监控cisco交换机 2950 2960s 3560G
转自: http://blog.chinaunix.net/uid-24250828-id-3806551.html 想在zabbix 上监控交换机端口的流量,找了两天的模板,包括官方的和网友写的.在 ...
- Luogu_2434_[SDOI2005]区间
题目描述 现给定n个闭区间[ai, bi],1<=i<=n.这些区间的并可以表示为一些不相交的闭区间的并.你的任务就是在这些表示方式中找出包含最少区间的方案.你的输出应该按照区间的升序排列 ...
- 用了python多进程,我跑程序花费的时间缩短了4倍
应用场景:本人需要对200万条网页html格式数据进行清洗,提取文字后将分词结果写入数据库,之前做了一次,大概花费了80多个小时才跑完.机器配置是4核,内存8G:开完会领导让再改点东西重新跑一遍,然后 ...
- github 下载部分代码
作者:知乎用户链接:https://www.zhihu.com/question/25369412/answer/96174755来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注 ...
- 从社交到IP 庞大手游玩家大军迈向社群化之路
庞大手游玩家大军迈向社群化之路" title="从社交到IP 庞大手游玩家大军迈向社群化之路"> 移动互联网及相关智能设备的快速迭进,不仅改变了我们的生活方式,也彻 ...