Generator和Async
引言
接触过Ajax请求的会遇到过异步调用的问题,为了保证调用顺序的正确性,一般我们会在回调函数中调用,也有用到一些新的解决方案如Promise
相关的技术。
在异步编程中,还有一种常用的解决方案,它就是Generator
生成器函数。顾名思义,它是一个生成器,它也是一个状态机,内部拥有值及相关的状态,生成器返回一个迭代器Iterator
对象,我们可以通过这个迭代器,手动地遍历相关的值、状态,保证正确的执行顺序。
Iterator接口
什么是Iterator接口
遍历器(Iterator
)就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署Iterator
接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。
Iterator的作用
- 为各种数据结构,提供一个统一的、简便的访问接口
- 使得数据结构的成员能够按某种次序排列
- ES6 创造了一种新的遍历命令
for...of
循环,Iterator
接口主要供for...of
消费。
Iterator实现
function makeIterator(array) {
var nextIndex = 0;
return {
next: function() {
return nextIndex < array.length ?
{value: array[nextIndex++], done: false} :
{value: undefined, done: true};
}
};
}
var it = makeIterator(['a', 'b']);
it.next() // { value: "a", done: false }
it.next() // { value: "b", done: false }
it.next() // { value: undefined, done: true }
原生具备Iterator接口的数据结构
- Array
- Map
- Set
- String
- TypedArray
- 函数的 arguments 对象
- NodeList 对象
查看一下Map
下面的所挂载的Iterator
。
let map = new Map();
console.log(map.__proto__);
输出结果:
clear:ƒ clear()
constructor:ƒ Map()
delete:ƒ delete()
entries:ƒ entries()
forEach:ƒ forEach()
get:ƒ ()
has:ƒ has()
keys:ƒ keys()
set:ƒ ()
size:(...)
values:ƒ values()
Symbol(Symbol.iterator):ƒ entries()
Symbol(Symbol.toStringTag):"Map"
get size:ƒ size()
__proto__:Object
如何为Object部署一个Iterator接口
function iteratorObject(obj){
let keys = Object.keys(obj);
let index = -1;
return {
next(){
index++;
return index<keys.length?{
value:obj[keys[index]],
key:keys[index],
done:false
}:{
value:undefined,
key:undefined,
done:true
}
}
}
}
let obj = {a:1,b:2,c:3};
let iter = iteratorObject(obj);
console.log(iter.next());
// {value: 1, key: "a", done: false}
console.log(iter.next());
// {value: 2, key: "b", done: false}
console.log(iter.next());
// {value: 3, key: "c", done: false}
console.log(iter.next());
// {value: undefined, key: undefined, done: true}
通过上面的方法可以简单的为Object
部署了一个Iterator
接口。
Generator函数
Generator是ES6的新特性,通过yield
关键字,可以让函数的执行流挂起,那么便为改变执行流程提供了可能。
Generator语法
dome:
function * greneratorDome(){
yield "Hello";
yield "World";
return "ending";
}
let grenDome = greneratorDome();
console.log(grenDome)
上面的代码中定义了一个Generator
函数,获取到了函数返回的对象。下面是其输出结果。
原型链:
greneratorDome {<suspended>}
__proto__:Generator
__proto__:Generator
constructor:GeneratorFunction {prototype: Generator, constructor: ƒ, Symbol(Symbol.toStringTag): "GeneratorFunction"}
next:ƒ next()
return:ƒ return()
throw:ƒ throw()
Symbol(Symbol.toStringTag):"Generator"
__proto__:Object
[[GeneratorStatus]]:"suspended"
[[GeneratorFunction]]:ƒ* greneratorDome()
[[GeneratorReceiver]]:Window
[[GeneratorLocation]]:test.html:43
[[Scopes]]:Scopes[3]
通过上面的输出结果可以看的出来,沿着原型链向上查找就存在一个next
方法,这个方法与Iterator
接口返回的结果是大同小异的。
继续延续dome代码,并使用next
方法向下执行。
function * greneratorDome(){
yield "Hello";
yield "World";
return "Ending";
}
let grenDome = greneratorDome();
console.log(grenDome.next());
// {value: "Hello", done: false}
console.log(grenDome.next());
// {value: "World", done: false}
console.log(grenDome.next());
// {value: "Ending", done: true}
console.log(grenDome.next());
// {value: undefined, done: true}
在最开始的地方有提到过Generator
函数,最后返回的是一个Iterator
对象,这也就不难理解了。
异步的Generator
dome
function a (){
setTimeout(() => {
alert("我是后弹出");
},1000)
}
function b (){
alsert("我是先弹出");
}
function * grenDome (){
yield a();
yield b();
}
let gren = grenDome();
gren.next();
gren.next();
// 输出结果
// 我是先弹出
// 我是后弹出
结合Promise
function a (){
return new Promise((resolve,reject) => {
setTimeOut(() => {
console.log(1)
resolve("a");
})
})
}
function b (){
return new Promise((resolve,reject) => {
console.log(2)
resolve("b");
})
}
function * grenDome (){
yield a();
yield b();
return new Promise((resolve,reject) => {
resolve("grenDome内部")
})
}
let gren = grenDome();
// console.log(gren.next())
// {value: Promise, done: false}
// console.log(gren.next())
// {value: Promise, done: false}
// console.log(gren.next())
// {value: Promise, done: true}
// console.log(gren.next())
// {value: undefined, done: true}
gren.next().value.then((res) => {
console.log(res);
// a函数
})
gren.next().value.then((res) => {
console.log(res);
// b函数
})
gren.next().value.then((res) => {
console.log(res);
// grenDome内部
})
// 输出结果
// a
// b
// grenDome内部
在上面的代码中有一点是需要注意的,在grenDome
函数里面最后return
出去了一个Promise
,但是在输出的时候虽然done
属性已经为true
但是value
里面仍然会存有一个promise
对象,实际上done
表示的是对应yield
关键字的函数已经遍历完成了。
Async/Await
Async/await
是Javascript
编写异步程序的新方法。以往的异步方法无外乎回调函数和Promise
。但是Async/await
建立于Promise
之上,换句话来说使用了Generator
函数做了语法糖。
async
函数就是隧道尽头的亮光,很多人认为它是异步操作的终极解决方案。
什么是Async/Await
async
顾名思义是“异步”的意思,async
用于声明一个函数是异步的。而await
从字面意思上是“等待”的意思,就是用于等待异步完成。并且await
只能在async
函数中使用。
Async/Await语法
function timeout(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
};
async function asyncPrint(value, ms) {
await timeout(ms);
console.log(value);
};
asyncPrint('hello world',2000);
// 在2000ms之后输出`hello world`
返回Promse对象
通常async
、await
都是跟随Promise
一起使用的。为什么这么说呢?因为async
返回的都是一个Promise
对象同时async
适用于任何类型的函数上。这样await
得到的就是一个Promise
对象,如果不是Promise
对象的话那async
返回的是什么就是什么。
async function f() {
return 'hello world';
}
f().then(v => console.log(v));
// hello world
async
函数返回一个Promise
对象,可以使用then
方法添加回调函数。当函数执行的时候,一旦遇到await
就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。
function a(){
return new Promise((resolve,reject) => {
console.log("a函数")
resolve("a函数")
})
}
function b (){
return new Promise((resolve,reject) => {
console.log("b函数")
resolve("b函数")
})
}
async function dome (){
let A = await a();
let B = await b();
return Promise.resolve([A,B]);
}
dome().then((res) => {
console.log(res);
});
执行机制
前面已经说过await
是等待的意思,之后等前面的代码执行完成之后才会继续向下执行。
function a(){
return new Promise((resolve,reject) => {
resolve("a");
console.log("a:不行")
})
}
function b (){
return new Promise((resolve,reject) => {
resolve("b");
console.log("b:不行");
})
}
async function dome (){
await a();
await b();
console.log("虽然我在后面,但是我想要先执行可以么?")
}
dome();
// 输出结果
// a:不行
// b:不行
// 虽然我在后面,但是我想要先执行可以么?
另外一个列子
function timeout1(ms) {
return new Promise((resolve) => {
setTimeout(() => {
console.log("timeout1")
resolve();
},ms);
});
};
function timeout2(ms) {
return new Promise((resolve) => {
setTimeout(() => {
console.log("timeout2");
resolve();
},ms);
});
};
async function asyncPrint() {
await timeout1(1000);
await timeout2(2000);
};
asyncPrint().then((res) => {
console.log(res);
}).catch((err) => {
console.log(err)
})
// 1s 后输出timeout1
// 3s 后输出timeout2
// undefined
async、await错误处理
JavaScript异步请求肯定会有请求失败的情况,上面也说到了async返回的是一个Promise对象。既然是返回一个Promise对象的话那处理当异步请求发生错误的时候我们就要处理reject的状态了。
在Promise中当请求reject的时候我们可以使用catch。为了保持代码的健壮性使用async、await的时候我们使用try catch来处理错误。
async function catchErr() {
try {
const errRes = await new Promise((resolve, reject) => {
console.log(a)
});
} catch(err) {
console.log(err);
}
}
catchErr();
// ReferenceError: a is not defined
总结
# Iterator接口
遍历器对象除了具有next
方法,还可以具有return
方法和throw
方法。如果你自己写遍历器对象生成函数,那么next
方法是必须部署的,return
方法和throw
方法是否部署是可选的。
Es6
提供很多API
都是基于Iterator
接口,比如解构,for...of循环,拓展运算等。
# Generator函数
调用Generator
函数,返回一个遍历器对象,代表Generator
函数的内部指针。以后每次调用遍历器对象的next
方法,就会返回一个有着value
和done
两个属性的对象。 value
属性表示当前的内部状态的值,是yield
语句后面那个表达式的值;done
属性是一个布尔值,表示是否遍历结束
# Async/Await
Async/await
是近些年来JavaScript
最具革命性的新特性之一。他让读者意识到使用Promise
存在的一些问题,并提供了自身来代替Promise
的方案。他使得异步代码变的不再明显,我们好不容易已经学会并习惯了使用回调函数或者then
来处理异步。
Generator和Async的更多相关文章
- JavaScript异步编程:Generator与Async
从Promise开始,JavaScript就在引入新功能,来帮助更简单的方法来处理异步编程,帮助我们远离回调地狱. Promise是下边要讲的Generator/yield与async/await的基 ...
- JS异步编程 (2) - Promise、Generator、async/await
JS异步编程 (2) - Promise.Generator.async/await 上篇文章我们讲了下JS异步编程的相关知识,比如什么是异步,为什么要使用异步编程以及在浏览器中JS如何实现异步的.最 ...
- generator 到 async 的简单理解。
generator 到 async 的简单理解.觉得实现方式很有意思. 1. generator generator 函数返回一个遍历器对象 遍历器对象 每次调用next 方法 返回 有着value ...
- ES Next & Arrow function & Promise & Iterator & Generator yield & Async Await
ES Next & Arrow function & Promise & Iterator & Generator yield & Async Await co ...
- ES 6 proimse &&iterator &&Generator函数 &&async
1.proimse 异步调用function getData(){ let promise =new Promise((resolve,reject)); let xmlHttp =new XMLHt ...
- Generator与async/await与Generator的模拟
Generator 函数有多种理解角度.语法上,首先可以把它理解成,Generator 函数是一个状态机,封装了多个内部状态. 执行 Generator 函数会返回一个遍历器对象,也就是说,Gener ...
- Promise,Generator,Await/Async
上节中忘记讲:Iterator接口和Generator函数的关系了,Symbol.iterator方法的最简单的实现就是通过Generator函数: let myIterable = { [Symbo ...
- Promise、Generator,Async/await
我们知道JavaScript是单线程语言,如果没有异步编程非得卡死. 以前,异步编程的方法有下面四种 回调函数 事件监听 发布/订阅 Promise对象 现在据说异步编程终极解决方案是——async/ ...
- Promise、Generator、Async有什么区别?
前言 我们知道Promise与Async/await函数都是用来解决JavaScript中的异步问题的,从最开始的回调函数处理异步,到Promise处理异步,到Generator处理异步,再到Asyn ...
随机推荐
- 【bzoj 3131】[Sdoi2013]淘金
Description 小Z在玩一个叫做<淘金者>的游戏.游戏的世界是一个二维坐标.X轴.Y轴坐标范围均为1..N.初始的时候,所有的整数坐标点上均有一块金子,共N*N块. 一阵风吹 ...
- Lucene 源码分析之倒排索引(三)
上文找到了 collect(-) 方法,其形参就是匹配的文档 Id,根据代码上下文,其中 doc 是由 iterator.nextDoc() 获得的,那 DefaultBulkScorer.itera ...
- 深度学习之卷积神经网络(CNN)
卷积神经网络(CNN)因为在图像识别任务中大放异彩,而广为人知,近几年卷积神经网络在文本处理中也有了比较好的应用.我用TextCnn来做文本分类的任务,相比TextRnn,训练速度要快非常多,准确性也 ...
- ReentrantLock之非公平锁源码分析
本文分析的ReentrantLock所对应的Java版本为JDK8. 在阅读本文前,读者应该知道什么是CAS.自旋. 由于ReentrantLock的公平锁和非公平锁中有许多共同代码,本文只会对这两种 ...
- Windows环境下消息中间件RabbitMq的搭建与应用
前言 消息中间件目前已经在很多大型的项目上得到了运用,我们常见的有 RabbitMq, activitymq,kafka,rocketmq,其中rocketmq是阿里自己在kafka的基础上用java ...
- Java语言编程 - 搭建Java开发环境
2.1 JDK.JRE和JVM关系 要弄清楚JDK.JRE和JVM这三者之间的关系,先看如下图,有个感性的认识: JDK:Java Development ToolKit(Java开发工具包).JDK ...
- nodejs操作redis总结
本文总结常见的使用node操作redis服务,redis的key是唯一的,如果一个key所对应的存储类型是string,则不能再次覆盖式设置key为hash; 1. 启动redis 这里我们使用doc ...
- Kubernetes的DaemonSet(下篇)
用Daemon Pod来进行通信 使用Pod来再DaemonSet中通信的手段有: 推的方式:在DaemonSet中的Pod会被配置成发送更新到如状态数据库这样的服务.这些都没有客户端. IP+端口方 ...
- Struts2的拦截器配置
1:引入默认的时间拦截器 <!-- 引入拦截器和引入拦截器栈一样的语法 --> <interceptor-ref name="defaultStack">& ...
- .net mvc + layui做图片上传(二)—— 使用流上传和下载图片
摘要:上篇文章写到一种上传图片的方法,其中提到那种方法的局限性,就是上传的文件只能保存在本项目目录下,在其他目录中访问不到该文件.这与浏览器的安全性机制有关,浏览器不允许用户用任意的路径访问服务器上的 ...