Promise 基础
官方文档:
一篇讲解原理很好的文章:
Promise的出现是为了解决回调地狱. 减少代码的嵌套循环,转而写成类似于串联的代码.
如今绝大部分的浏览器都支持Promise了.我们可以在控制台中输出看一下.
可以看到Promise()
大写的P说明,这是一个构造函数.我们需要传入带有resolve
和reject
的函数
而在prototype
中我们也能看then
和catch
这两个函数.这两个函数是解决回调地狱的关键.
原理性的知识,可以移步我给的链接.下文主要结合代码讲一下使用.
Promise 使用
Promise 构造器
Promise是对异步操作进行处理,而JS的执行顺序是先执行主程序,后进行异步操作
为了方便演示, 使用了setTimeout()
来模拟异步请求
先看一下下面的代码
new Promise((resolve, rejcet) => {
setTimeout(() => {
console.log('Promise')
}, 2000)
console.log('in Promise')
});
console.log('out Promise')
输出结果
in Promise
out Promise
Promise
如此,我们可以知道当 new Promise 时,里面的代码会立即执行.
那么如何让我们的代码在想要的适当的时机才去执行Promise里的操作呢?
我们可以将 new Promise写到一个函数中去,然后在需要的时候进行调用.例如下面的代码:
function testPromise() {
return new Promise((resolve, rejcet) => {
setTimeout(() => {
console.log('Promise')
}, 2000)
console.log('in Promise')
});
}
console.log('out Promise')
testPromise()
输出结果
out Promise
in Promise
Promise
构造器函数
new Promise((resolve, rejcet) => {... }
可以看到new Promise时传入了带有两个参数的函数.
其中 resolve
是当异步方法成功时执行的,而reject
是失败时调用的
下面的代码中,我写了一个 定时器并结合 ajax()
用来模拟一次异步请求,其中第一个参数flag
用来模拟是否出现异常,
大家可以先大致了解一下,下面会讲到它的执行顺序
// 模拟ajax请求 第一个参数来模拟是否出现异常
function ajax(flag, err, data, resolve, reject) {
// return 并不会起到返回的作用,只是为了代码到此结束
if (flag == false)
return reject(err)
resolve(data)
}
function testPromise(err, data, flag) {
return new Promise((resolve, reject) => {
// 设置ajax(ture/false,...)来决定是否有异常
setTimeout(ajax(flag, err, data, resolve, reject), 2000)
})
}
testPromise('出错了 err', '没有错误,返回数据data', true)
.then((data) => {
console.log('----回调resolve :' + data)
})
.catch(err => {
console.log('----回调reject :' + err)
})
执行顺序
上面的代码,咋一看很是乱糟糟的,有些逻辑混乱.下面梳理一下执行的顺序
- testPromise(‘出错了 err’, ‘没有错误,返回数据data’) 这行代码会调用
testPromise(err, data)
- 上一步中的 new Promise((resolve, reject) =>{}) 中的
resolve
和reject
并不会有任何值.并立即返回一个Promise
对象- 此时,虽然返回了一个 Promise对象,但是
resolve
和reject
还是没有具体的函数的,只是起到占位作用.后面会进行注入 - 而setTimeout(ajax(false, err, data, resolve, reject), 2000) 是一个延迟两秒的定时器,JS等2s会再执行它(模拟异步)
- 此时,虽然返回了一个 Promise对象,但是
- 接下来回到 .then(data=>{…})
then()
会传入一个具体的函数来来执行成功后异步方法.也就是注入resolve
,而.catch(err => {}) 会注入reject
- 此时resolve, reject都拥有了具体的函数,上面的步骤都是JS会极快的完成的
- 2s后,执行到setTimeout()
- ajax()的flag==false 继而会执行 return reject(err) 就回调了.catch(err => {})
- 输出console.log(’----回调reject :’ + err)
具体的使用
通过上面的代码和解释,我们已经看到Promise的特别之处,但是具体怎么才能解决回调地狱呢?
testPromise('出错了 err', '没有错误,返回数据data', true)
.then((data) => {
console.log('----回调resolve :' + data)
return testPromise('哈哈哈,又错了', '这一次还没有错', true)
})
.then(data => {
console.log('----回调resolve :' + data)
})
.catch(err => {
console.log('----回调reject :' + err)
})
输出结果
----回调resolve :没有错误,返回数据data
---回调resolve :这一次还没有错
必须还要返回一个Promise
实例才可以再次使用.then()
方法
这样,我们就可以看到原来代码的嵌套, 已经通过 .then()
串联到一起了.
只要上一个调用返回 Promise
实例 ,就可以无限.then()
下去
异常的处理
上面的代码都是通过 catch()
进行异常捕获的.
function testPromise(err, data) {
return new Promise((resolve, reject) => {
// 设置ajax(ture/false,...)来决定是否有异常
setTimeout(ajax(false, err, data, resolve, reject), 2000)
})
}
testPromise('出错了 err', '没有错误,返回数据data')
.then((data) => {
console.log('----回调resolve :' + data)
return testPromise('哈哈哈,又错了', '这一次还没有错')
})
.then(data=>{
console.log('----回调resolve :' + data)
})
.catch(err => {
console.log('----回调reject :' + err)
})
输出结果:
----回调reject :出错了 err
结果是只输出了一次错误的
我们由此可以知道 catch()
是只要Promise执行链中出现一次异常,之后的的代码就不会执行了
那么我们如何让Promise执行链中如果出现异常后面的代码仍然执行呢?
其实.then(function(){},function(){} )
.then()
可以传入两个函数
- 第一个函数用来处理正常执行的函数是必须传的
- 第二个函数用来处理异常,可以不穿.如果传了,.catch就不会进行处理,从而不影响Promise执行链
- 这两个函数都需要返回一个
Promise
实例,才能延续执行链
testPromise('出错了 err', '没有错误,返回数据data', false)
.then((data) => {
console.log('----回调resolve :' + data)
return testPromise('哈哈哈,又错了', '这一次还没有错', false)
},err =>{
console.log(err)
return testPromise('哈哈哈,又错了', '这一次还没有错', false)
})
.then(data => {
console.log('----回调resolve :' + data)
})
.catch(err => {
console.log('----回调reject :' + err)
})
输出结果
出错了 err
----回调reject :哈哈哈,又错了
axios
axios 是一个基于 promise 的 HTTP 库
它进行了很好的封装,尤其是一些常见的get,post请求.而且还有很好的拦截器机制
也有尤大神的推荐,因此大家可以了解一下.