ES6学习笔记(六)【promise,Generator】

简介

在ES6以前,我们处理异步操作只能使用回调函数

ES6中新增了几种书写异步代码的解决方案,promise是最常用的一种

正文

Promise是一个构造函数,我们可以用new关键字生成一个promise实例来使用

let promise = new Promise((resolve, reject) => {
        //做一些异步操作
        setTimeout(() => {
            console.log('success')
            resolve('promise is success')
        }, 2000)
    })

Promise构造函数接受一个函数作为参数,在这个函数内执行一些异步操作

这个函数拥有两个参数:resolve和reject,这两个都是函数,js引擎会帮你传入

在函数内部调用他们的时候分别代表对外声明异步操作已经成功(resolve)或失败(reject)

为什么会设计的这样复杂?

setTimeout(() => {
  return 1
}, 2000)

这是一个简单的回调方式处理异步操作的结果,但是回调函数被外层的定时器包裹

我们没法简单的拿到回调函数的返回值,这也是回调函数最大的缺陷

所以promise通过resolve和reject方法,对外传递一个可以轻易访问到信息

如果我们需要拿到并处理promise内部resolve的信息,需要使用then方法:

let promise = new Promise((resolve, reject) => {
        //做一些异步操作
        setTimeout(() => {
            console.log('success')
            resolve('promise is success')
        }, 2000)
    })

promise.then(x => console.log(x))
// promise is success

promise实例上的then方法是promise里最常用的方法,它接受一个函数为参数,这个函数的第一个参数就是这个promise内部resolve出来的值,从而我们可以在这个函数内部获取和使用这个值

另一个常用方法是catch,如果这个promise异步操作出了问题

我们会在函数内部调用reject方法传递出去错误信息,代表promise出错了

这时候应该使用catch方法来处理错误信息:

let promise = new Promise((resolve, reject) => {
        //做一些异步操作
        setTimeout(() => {
            console.log('error')
            reject('promise is error')
        }, 2000)
    })

promise.then(x => console.log(x))
// 报错 Uncaught (in promise) promise is success

promise.catch(x => console.log(x)
// 不报错 输出 promise is error

其实如果你给then方法传入两个函数,那么第二个函数也是可以捕获到这个错误的

catch方法只是then的一个别名,不过为了代码清晰易读

我们最好都是用catch,而不是给then传入两个参数

现在我们有then方法处理执行成功的promise,用catch处理出错的promise

显然需要一种无论成功还是出错都能处理的方法,就像try…catch中的finally

promise也提供了一个finally方法,用法与then,catch完全相同

无论promise成功还是失败都会执行finally中的回调函数

除了这四种promise实例上的方法以外,js还原生提供了Promise.resolve()和Promise.reject()方法

注意不是promise内部的resolve,reject方法,不要混淆

这两个方法可以简单的把一个异步操作包装成promise:

Promise.resolve('success')
// 等价于
new Promise((resolve, reject) => resolve('success'))

Promise.reject('error')
// 等价于
new Promise((resolve, reject) => reject('error'))

如果需要快速创建一个resolve或者reject状态的promise,就用这两个方法

promise构造函数也可以包装多个promise实例,来让一些异步操作并行执行

let all = Promise.all([p1, p2, p3])

let race = Promise.race([p1, p2, p3])

Promise.all和Promise.race方法都接收一个可遍历对象为参数,其中每一项都是一个promise实例

上面代码中 p1, p2, p3 都是一个promise实例,内部要执行一些异步操作

在Promise.all生成的promise对象中,p1, p2, p3都完成(resolve)的时候,

它自身才算完成,自动调用内部的resolve方法

而Promise.race生成的promise的对象中.只要p1, p2, p3有一个完成

它就算完成,立刻调用resolve方法

但是还没完成的也不会终止,最终三个promise也都会完成

思考

这部分内容希望你都可以手动敲一遍,独立思考

尝试用Promise封装一个发GET请求的方法,接受一个url字符串为参数

返回一个请求该URL的Promise,并且状态码为200时可以调用then方法获取返回数据

状态码为其他时可以在catch中输出报错信息


根据上面写出的请求封装函数生成三个请求不同url的Promise

并用Promise.race尝试做一个延迟处理,一秒内没有收到响应的请求就不再获取数据


Generator 是ES6新增的一种函数,可以看做一个状态机

通常我们通过Generator函数来实现一些异步操作

正文

定义一个Generator函数需要在function关键字后加一个*号

function *f() {
  yield 1
  yield 2
}
let a = f()
a.next()      // {value: 1, done: false}
a.next()      // {value: 2, done: false}
a.next()      // {value: undefined, done: true}
a.next()      // {value: undefined, done: true}

显然我们定义个一个叫做f的Generator函数,那么f可以看做是一个状态机

f的内部我们用 yield 关键字定义了两个 状态

然后我们通过f()执行这个函数,与普通函数不同的是,Generator函数执行后,

得到的结果并不是函数的返回值,而是而是一个代表其内部状态的Iterator

Iterator我们之前已经介绍过了,是一个通用的遍历器接口,我们可以调用它上面的next方法得到其内部的值

yield 命令用在Generator函数体中,与return类似,停止执行函数并把它后面的表达式的值返回

yield 把Generator函数体划分成一块一块来执行,每次调用next方法,都会使函数继续向下执行,直到遇见下一个 yield 并把值放在value里返回

如果后续再没有其他 yield 或return 就把done字段置为true,代表全部的状态都已运行完毕

正常情况下我们不会手动去一次次调用next查看结果,而是使用 for...of 循环:

function *f() {
  yield 1
  yield 2
}
for(let x of f()){
  console.log(x)
}
// 1 2

关于Generator的细节知识点很多,比如next还可以传入参数影响Generator函数内部状态等等

不过时至今日(17年6月),Generator在开发中的应用已经很少了,这里不再为新手介绍其他知识

主流的异步方案已经逐渐成型,目前我们使用Promise和Async来解决异步问题

思考

这部分内容希望你都可以手动敲一遍,独立思考

尝试写出一个表示斐波那契数列的Generator函数

函数接收一个参数,代表生成的斐波那契数列的长度

function* fib(length){
  // 做一些操作,然后使用yield返回每一步的结果
}
for(let x of fib(7)){
  console.log(x)
}
// 输出 [1, 1, 2, 3, 5, 8, 13]

猜你喜欢

转载自blog.csdn.net/WuLex/article/details/86231703
今日推荐