前言
今天我们介绍一下es6引入的几个比较重要的东西. 他们能够让我们改善在es6之前我们遇到的某一些问题的痛点. 那么废话不多说, 直接进入主题吧.
generator 生成器函数
generator(生成器)是ES6标准引入的新的数据类型。一个generator看上去像一个函数,但可以返回多次
function *gen () {
yield 1
yield 2
yield 3
}
let g = gen()
g.next() // {value: 1, done: false}
g.next() // {value: 2, done: false}
g.next() // {value: 3, done: false}
g.next() // {value: undefined, done: true}
生成器需要在function后加上一个*作为标识, 这里需要注意的是
function* gen() {}function * gen() {}function *gen() {}这三种写法都是合法的.generator生成器的行为是当你调用了next的时候,它就会走一步方法里边的yield的值. 如果走完了所有的yield的话,再调用next方法,则对返回一个value是undefined,done是true的对象. 我们其实可以理解成yield是函数里边的断点. 当我们调用了next方法的时候,它就会走到下一个yield断点上. 由于这个特点,我们可以写一个无线迭代器
function *getTime() {
while (true) {
yield new Date().toLocaleTimeString()
}
}
let getNow = getTime()
getNow.next()
大家可以尝试一下运行上面一段代码. 在我们执行getNow.next()的时候,我们永远可以拿到最新的本地时间.
Promise
Promise 对象用于表示一个异步操作的最终完成 (或失败)及其结果值
Promise顾名思义它是承诺的意思, 一般用于异步上,因为异步发生在将来, 我们无法预见未来,所以我们可以先给一个承诺. 这和我们现实生活是一样一样的. 西式婚礼上,司仪会问男方:
XX,你愿意娶XX小姐为你的妻子么?照顾她,爱护她,无论贫穷还是富有,疾病还是健康,相爱相敬,不离不弃,永远在一起. 男: 我愿意!
用法:
首先Promise是一个对象,我们使用它的时候,需要先new一下它, Promise需要传递一个回调函数,回调函数可以传递两个参数进去, 第一个是resolve,第二个是reject.讲到这里,我们先说明一下Promise的几种状态.
- 待定 (pending): 初始状态, 既没有被兑现,也没有拒绝
- 已兑现 (fulfilled) : 成功
- 已拒绝 (rejected) : 失败 下面我们一下代码了解一下具体的用法:
let p = new Promise((resolve, reject) => {
console.log('1')
resolve(true)
})
// 第一个p.then
p.then((val) => {
// resolve的时候走这里
console.log('2')
return val + ' [true]'
}, (err) => {
// reject的时候走这里
console.log(err)
}).then((val) => {
console.log(val + '[next then]')
})
// 第二个p.then
p.then((val) => {
console.log('3')
return val + ' [true]'
}, (err) => {
console.log(err)
}).catch((err) => {
console.log(err)
})
console.log('4')
// 1
// 4
// 2
// 3
// true [true][next then]
我们来分析一下上边我们写的实例.
- 一开始的
promise的状态是pending的 - 我们在回调函数内如果
resolve了一个值出去的话, 则状态会变成fulfilled,] - 如果走了
reject的方法的话,则状态会变成rejected. 需要注意的地方是,promise的状态是不可逆的,即: 一旦promise的状态从pending变成了其他的状态的话,则就永远是这个状态了. 我们可以看下一个例子:
let p = new Promise((resolve, reject) => {
resolve(true)
reject('err')
})
p.then((val) => {
console.log(val)
}, (err) => {
console.log(err)
})
// true
p.then方法接收两个回调函数,第一个是成功的回调函数,第二个是失败的回调函数.- 上面我们即执行了
resolve方法也执行了reject方法,但是最后只走了成功的方法,并且状态是fulfilled. 说明如果状态变成了fulfilled的话,则不会再改成状态成rejected了. - 如果是走错误回调方法的话,另外除了
reject一个值以外,我们还可以通过throw new Error('err')抛出一个错误的方式,走到失败回调函数里边.
我们再看到第一个例子里边. 我们在里边console了一些东西, 最后返回的是1,4,2,3. 这说明了几个问题.
new Promise里边走的回调函数是同步执行的p.then()里边的回调函数是异步执行的- 如果有两个
p.then()的话,他们会一起执行,顺序的话,则是按照代码的先走顺序执行.
Promise的其他方法
promise里边还有其他的很多方法,下面我们介绍一下比较常用的两个方法
all
Promise.all接收一个Promise的数组,返回的时候也是返回的一个Promise.then拿到的成功的值的数组. 下面我们看一下代码
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});
Promise.all([promise1, promise2, promise3]).then((values) => {
console.log(values);
});
// [3, 42, "foo"]
三个Promise放置在一个数组,传递给Promise.all, 等待三个Promise都成功的时候, 就会执行then函数拿到的值就是三个Promise成功返回的值. 值得一说的是,它必须要三个Promise都返回成功的时候,才会走then方法. Promise.all究竟有什么用呢? 我们来找一个它的使用场景, 比如有一个业务,需要满足两个条件,才会走下一步逻辑,而这两个逻辑可能都是异步的, 这时候,如果放在我们以前的话, 可能需要写一个全局的变量初始为0, 然后如果调用后台接口,返回成功的时候,我们去累加1,如果两个都成功的话,则是2. 因为不知道两个接口拿一个会先调用成功,所以我们需要在两个接口里边都写上一个函数, 如果计数的变量是2的情况,则走下一个逻辑. 这样写的代码,会有很多耦合的地方. 而且加上了全局变量污染, 并且我们还得考虑什么时候需要重置这个计数的变量. 写的代码就很容易出问题了. 但是有了Promise.all的话,我们就可以把两个接口都写在Promise.all里边,两个接口都返回成功了, 就会直接走then方法了.
race
Promise.race和Promise.all的写法基本上是一致的, 如果理解了上面的Promise.all的话,race就很好理解了, race是一个竞态的方法. 就是数组里边传入的Promise谁先成功返回了数据后,就会调用then方法了.
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});
Promise.race([promise1, promise2, promise3]).then((values) => {
console.log(values);
});
// 3
结语
今天我们复习了一下generator它的使用方法,以及讲了一下Promise的写法和它的其他方法以及使用场景. Promise这个东西,我们还是要理解的比较深刻,后边无论是面试还是工作都很有用. 很多的东西其实都是基于Promise的. 比如es6以后的async,await概念,以及目前新版浏览器原生支持的fetch. 好了, 今天就复习到这里咯. 下次见~