简介
在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]