工作中promsie使用非常频繁,项目中随处可见,有使用js的事件循环机制的异步等待,封装axios的网络请求等等
目录
promise 以前是为了解决回调地狱提出来的,至于回调地狱,举一个例子
一、promise的初级使用,无限链式调用(.then .catch .finally)
前言
promise 以前是为了解决回调地狱提出来的,至于回调地狱,举一个例子
<script>
setTimeout(function () {
//第一层 等5秒打印张三在执行下一个回调函数
console.log('张三');
//第二层 等4秒打印李四在执行下一个回调函数
setTimeout(function () {
console.log('李四');
//第三层 等三秒打印王五
setTimeout(function () {
console.log('王五');
//第四层 等二秒打印朱六
setTimeout(function () {
console.log('朱六');
//第五层 等一秒打印老七
setTimeout(function () {
console.log("...........是不是已经很可怕了,实际业务可能还会往下嵌套")
console.log('老七');
}, 1000)
}, 2000)
}, 3000)
}, 4000)
}, 5000)
/**
*输出的结果是符合预期的 张三 李四 王五 朱六 老七
*但是明显代码结构问题很大,会无限的往右延申
*/
</script>
输出的结果是符合预期的 张三 李四 王五 朱六 老七
但是明显代码结构问题很大,会无限的往右延申
怎么样,是不是很可怕了,实际业务中,可能还会无限的往下嵌套下去,如果再{}方法块里面再写一写其他逻辑,那么整个代码会变的很难维护。
因为axios时异步的,所以很多dom操作和赋值操作,必须写在对应的函数体内,这样就会形成一个回调地狱,无限的嵌套下去。
这个时候promise登场了,对应的还有async和await
一、promise的初级使用,无限链式调用(.then .catch .finally)
示例:为了解决回调地狱的问题,我们看看promise的简单应用
Promise 和Array、String 一样是内置对象可以直接使用
/**
* Promise 和Array、String 一样是内置对象可以直接使用
* 可以直接使用 resolve reject 来进入 .then 和 .catch
* res 是回调接收的值 reslove 和 reject 传入的值
*/
Promise.resolve('第一层嵌套 张三').then(res =>{
console.log(res)
//return出去的值,是下一个回调的参数
return '第二层嵌套 李四'
}).then(res =>{
console.log(res)
return '第三层嵌套 王五'
}).then(res =>{
console.log(res)
return '第四层嵌套 朱六'
}).then(res =>{
console.log(res)
return Promise.reject('第五层嵌套 老七')
}).catch(err => {
//catch结束promise状态 不能再继续.then
console.log(err)
}).finally(() => {
//无论成功失败都会执行 执行最后一次操作
console.log('无论成功失败都会执行')
})
只要是一个promise对象,就可以再对象后面.then .catch 。then是成功的回调,catch是失败的。
看上去有没有简洁很多,虽然他还是一层包裹一层,但是不会一直再方法块里面一直嵌套下去,代码的结构看上去清晰了很多,一目了然。
你可以把我上面的代码,复制下去,再自己的控制台里运行一下。根据我写的注释,自己理解一下
二、promise 可以无限链式调用的原理
promise可以无限调用,是因为.then的时候,内部返回了一个promise对象。
只要是promise对象,就可以使用.then .catch等方法。
promise内部原理很复杂,我跟大家举一个简单的例子
class MyPromise{
resolve(){
}
reject(){
}
then(){
//执行.then方法的时候 对外返回一个promsie
return new MyPromise()
}
catch(){
}
}
在.then的时候,返回了一个promise。就类似jquery里面经典的链式调用。在调用一个方法返回一个jquery对象一样。promise内部也是如此
三 promise实际应用
代码如下(示例):最简单的列子,对axios封装套了一层promise
import axios from 'axios'
const service = axios.create({
// baseURL: 'https://zptest-api.yocyxc.com', // url = base api url + request url
baseURL: baseApi,
// withCredentials: true, // send cookies when cross-domain requests
timeout: 30000 // request timeout
})
// request拦截器 request interceptor
service.interceptors.request.use(
async config => {
// 请求头
config.headers = {
...config.headers,
}
return config
},
error => {
// do something with request error
console.log(error) // for debug
return Promise.reject(error)
}
)
// respone拦截器
service.interceptors.response.use(
response => {
//重点,接口请求成功后,axios会返回数据,这个时候我们return一个promsie对象
//
return new Promise((reslove,reject) => {
if(response.code === 200){
reslove(response)
}else{
reject(response)
}
})
},
error => {
console.log('🚀 request - error', error)
Toast.clear()
if (flagToast) {
Toast(error.message)
}
return Promise.reject(error)
}
)
export default service
定义一个promise对象的api接口,引入的requst就是我们封装的,在响应拦截的时候,retrun返回的是一个promsie对象,所以外面的接口调用就可以.then .catch
//上面代码封装的路径,引入进来
import request from '@/utils/request'
// 用户信息
export function userInfo(params) {
return request({
url: '接口地址',
method: 'get', //请求类型
params //传进来的参数 如果是post请求,参数为data
})
}
文件里面使用,就是引入,在调用就可以,什么框架都可以
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
</body>
<script>
//引入我们封装的接口
import {userInfo} from './api/user.js'
function getUserInfo(){
userInfo({id:1}).then(res => {
//userInfo是一个我们封装的promise对象
//可以在后面进行.then 和catch
console.log(res)
}).catch(err => {
console.log(err)
})
}
getUserInfo()
</script>
</html>
四 promise执行顺序
说到执行顺序,需要先了解一个eventLoop,是一个事件循环机制
js本身是单线程,但是浏览器却不是。js是从上到下执行,遇到异步代码会放到事件队列里面,
等同步代码执行完成之后,才会执行。
- 宏任务:promsie nextTick
- 微任务:setTimeout setInterval
同步代码 > 宏任务 > 微任务
下面举个例子,大家就理解了
console.log(1)
const p = new Promise((resolve) => {
//promise里面的代码是同步的
console.log(2)
//resolve出去回调是异步的
resolve()
})
setTimeout(() => {
//setTimeout是微任务,最后执行
console.log(4)
}, 0)
//promsie是宏任务 优先级高于微任务的定时器 .then是异步
p.then(() => console.log(5))
console.log(3)
//结果是 1 2 3 5 4
顺序是 1 2 4 5 3
执行的结果是 1 2 3 5 4
因为里面的4是微任务,最后执行,5是宏任务,在同步代码后执行
如果涉及多个微任务和宏任务,就看谁先执行。
evevtLoop类似一个对象,有两个数组,一个是宏任务,一个是微任务
遇到异步事件就往里push,然后再弹出执行
总结
- promise对象都可以使用.then .catch
- promise是js内置对象,还有promise.all等方法
- promise能一直链式调用.then的原理是 .then的时候,内部返回了一个promise对象
- promise是异步事件,会放到事件队列里,promsie是微任务,优先级高于宏任务(定时器)
- promise的应用很多,因为有异步机制,又有类似于try () catch() 的异常捕捉,可以借住闭包和异步的特性,完成很多事情。
当然promise的知识远不止如此,promise的源码大家有兴趣可以去看看。