本文首发CSDNjavascript
其中关于异步操做的处理就引入了Promise和生成器。众所周知,Promise能够在必定程度上解决臭名昭著的回调地狱问题。可是在处理多个异步操做时采用Promise链式调用的语法也会显得不是那么优雅和直观。而生成器在Promise的基础上更进一步,容许咱们用同步的方式来描述咱们的异步流程。java
generator函数是ES6中一个特殊函数,经过 function* 声明,函数体内经过 yield 来指明函数的暂停点,该函数返回一个迭代器,而且函数执行到 yield语句前面暂停,以后经过调用返回的迭代器next()方法来执行yield语句。npm
function* generator() {
yield 1;
yield 2;
yield 3
}
var gen = generator()
复制代码
generator 函数就是ES6中的生成器,生成器又能够生成迭代器,代码执行中断,不会一下执行完,这样咱们就能够用同步的方式来描述咱们的异步流程。promise
调用generator函数后,该函数并不执行,返回的也不是函数运行的结果,而是一个指向内部状态的指针对象,咱们能够经过调用next方法,使指针移向下一个状态。bash
console.log(gen.next()) //{ value: 1, done: false }
console.log(gen.next()) //{ value: 2, done: false }
console.log(gen.next()) //{ value: 3, done: false }
console.log(gen.next()) //{ value: undefined, done: true }
复制代码
Generator 能够实例化出一个 iterator ,而且这个 yield 语句就是用来中断代码的执行的,也就是说,配合 next() 方法,每次只会执行一个 yield 语句。异步
yield 后面能够是任意合法的JavaScript表达式,yield语句能够出现的位置能够等价于通常的赋值表达式(好比a=3)可以出现的位置。async
b = 2 + a = 3 // 不合法
b = 2 + (a = 3) // 合法
b = 2 + yield 3 // 不合法
b = 2 + (yield 3) // 合法
复制代码
复制代码yield关键字的优先级比较低,几乎yield以后的任何表达式都会先进行计算,而后再经过yield向外界产生值。并且yield是右结合运算符,也就是说yield yield 123等价于(yield (yield 123))函数
function* generator() {
yield 1
try {
yield 2
} finally {
yield 3
}
}
let genl = generator()
console.log(genl.next()) //{ value: 1, done: false }
console.log(genl.next()) //{ value: 2, done: false }
console.log(genl.return(4)) //{ value: 3, done: false }
console.log(genl.next()) //{ value: 4, done: true }
let gen =generator()
console.log(gen.next()) //{ value: 1, done: false }
console.log(gen.return(4)) //{ value: 4, done: true }
console.log(gen.next()) //{ value: undefined, done: true }
console.log(gen.next()) //{ value: undefined, done: true }
复制代码
return 会终结整个 Generator ,换句话说:写在 return 后面的 yield 不会执行。 它可以中断执行代码的特性,能够帮助咱们来控制异步代码的执行顺序ui
分析如下码哥:spa
function* generator() {
let result = yield 'hello'
console.log(result)
}
var gen = generator()
// console.log(gen.next()) // { value: 1, done: false }
console.log(gen.next(22)) //{ value: 1, done: false }
console.log(gen.next(2)) //2 { value: undefined, done: true }
复制代码
第一次调用next方法传入的参数,生成器内部是没法获取到的,或者说没有实际意义,由于此时生成器函数尚未开始执行,第一次调用next方法是用来启动生成器函数的。
const fs = require("fs").promises;
// 生成器
function * read(){
yield fs.readFile("./name.txt","utf-8")
}
// 迭代器
let it = read()
// console.log(it.next()) // { value: Promise { <pending> }, done: false }
it.next().value.then(data=>{
console.log(data) // name
})
复制代码
这是简单的generator 实例
开始变形 文件: name.txt: age.txt age.txt: 666
const fs = require("fs").promises;
function * read(){
let concent = yield fs.readFile("./name.txt","utf-8")
let age = yield fs.readFile(concent,"utf-8")
return age
}
let it = read()
it.next().value.then(data=>{
it.next(data).value.then(data=>{
let r = it.next(data)
console.log(r) // { value: '666', done: true }
})
})
复制代码
才两个就这么麻烦,还好有大神TJ的co库 下载
npm install co
复制代码
co 是著名大神 TJ 实现的 Generator 的二次封装库
const fs = require("fs").promises;
function * read(){
let concent = yield fs.readFile("./name.txt","utf-8")
let age = yield fs.readFile(concent,"utf-8")
return age
}
let co = require("co")
co(read()).then(data=>{
console.log(data) // 666
})
复制代码
generator 语法糖 async/await很是好用,代码更简洁了!
const fs = require("fs").promises;
async function read() {
let concent = await fs.readFile("./name.txt", "utf-8")
let age = await fs.readFile(concent, "utf-8")
return age
}
read().then(data => {
console.log(data) // 666
})
复制代码
ES7引入的async/await语法是Generator函数的语法糖,只是前者再也不须要执行器。直接执行async函数就会自动执行函数内部的逻辑。async函数执行结果会返回一个Promise对象,该Promise对象状态的改变取决于async函数中await语句后面的Promise对象状态以及async函数最终的返回值。接下来重点讲一下async函数中的错误处理。
await关键字以后能够是Promise对象,也能够是原始类型值。若是是Promise对象,则将Promise对象的完成值做为await语句的返回值,一旦其中有Promise对象转化为Rejected状态,async函数返回的Promise对象也会随之转化为Rejected状态
async function aa() {await Promise.reject('error!')}
aa().then(() => console.log('resolved'), e => console.error(e)) // error!
复制代码
若是await以后的Promise对象转化为Rejected,在async函数内部能够经过try...catch捕获到对应的错误。
async function as() {
try {
await Promise.reject('error!')
} catch(e) {
console.log(e)
}
}
as().then(() => console.log('resolved'), e => console.error(e))
// error!
// resolved
复制代码
若是async函数中没有对转化为Rejected状态的Promise进行捕获,则在外层对调用aa函数进行捕获并不能捕获到错误,而是会把aa函数返回的Promise对象转化为Rejected状态
从一开始的回调函数,到社区大佬们提出后来又加入ES6的Promise,再有generator的生成迭代,到TJ大神的co 库,JavaScript中的代码虽然是单线程的,异步问题的解决方式愈来愈强,generator的语法糖 async和await 和promise结合是如今的主流也是及有效的方式。