本人前端小白,项目经历较少,遇到页面接口请求较多时总会有些疑惑,不知道怎么处理合理。看之前的项目代码,写了不知多少回调,最近整理promise.all和async/await两个方法,分享一下。同时结合两者优化下接口性能,减少请求耗时。
Promise
众所周知,Promise 是一个对象,用于表示一个异步操作的最终完成 (或失败), 及其结果值。
语法:new Promise( (resolve, reject) => {...} /* executor */ );
let promise = new Promise((resolve,reject) => {
resolve('success')
// or
reject('error')
})
该对象接收两个参数
- resolve: 将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去.
- reject: 将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
promise.then(res => {
console.log(res)
}).catch(err => {
console.log(err)
})
then方法会返回一个Promise,接收一个回调函数作参数,回调函数接受Promise对象传出的值作为参数。
Promise.all()
将多个promise实例包装成一个新的promise实例,接收一个数组作参数。成功时返回一个数组,数组中包括所有子promise的结果;失败时,返回第一个失败promise的结果。
let p1 = new Promise((resolve,reject) => {
resolve('success1')
//reject('error')
})
let p2 = new Promise((resolve,reject) => {
resolve('success2')
})
let p3 = new Promise((resolve,reject) => {
reject('error')
})
成功状态时:
Promise.all([p1,p2]).then(res => {
console.log(res)
}).catch(err => {
console.log(err)
})
// 返回结果
['success1', 'success2' ]
失败时:
Promise.all([p1,p2,p3]).then(res => {
console.log(res)
}).catch(err => {
console.log(err)
})
// 输出
error
注意:
- 成功时返回数组中,值的顺序跟传入参数中promise实例的顺序对应,类似于同步执行,不会因为返回结果耗时的影响。
适用场景:
- 页面请求过多时,可根据Promise.all()返回结果判断所有请求是否成功,否则使页面一直处于loading状态,避免用户操作,或者提是错误。
- 发送多个请求,需要根据请求顺序获取和使用数据的场景。
- 避免promise使用不合理或各种回调函数造成地狱回调,无法维护。
Promise.race()
用法同Promise.all(),哪个子promise结果先被返回,就返回该结果,不管是成功还是失败状态。
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success')
},1000)
})
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('error')
}, 500)
})
Promise.race([p1, p2]).then((result) => {
console.log(result)
}).catch((error) => {
console.log(error) // 'error'
})
async/await
- 见名知意: 异步与等待
- 同步思维解决异步问题的方案。
async
函数使用async后将转换成Promise,并且其返回值也是一个Promise。
async function fn() {
console.log('async')
return 1
}
fn()
// 结果
async
Promise {<resolved>: 1}
如果函数内部未使用return,resolved的值为undefind。(最好在浏览器上尝试,node上有返回promise,但无法显示,可能我的node4版本较低)
- 既然返回值是Promise,那我们变可以使用then()获取。
async function fn() {
console.log('async')
return 1
}
fn().then(res => {
console.log(res) // 1
})
await
- await只能在async函数内部使用,用在普通函数里就会报错
结合实际开发来说明
fetch('https://blog.csdn.net/')
.then(res => {
console.log(res)
return fetch('https://juejin.im/')
})
.then(res => {
console.log(res)
})
.catch(error => {
console.log(error)
})
- 这种代码可能大家都有见过,虽然能体现等待的效果,但当链式调用过多,将充斥着多个then,配合业务逻辑那就部那么语义化,且难以维护。
async function foo () {
try {
let response1 = await fetch('https://blog.csdn.net/')
console.log(response1)
let response2 = await fetch('https://juejin.im/')
console.log(response2)
} catch (err) {
console.error(err)
}
}
foo()
- 上面代码,整个异步处理的逻辑都是使用同步代码的方式来实现的,感觉就在写同步代码,比较符合人的线性思维的。
- await 后面一定是Promise 对象,如果不是会自动包装成Promise对象。
promise.all与async/await结合,优化请求
先看个栗子
function fn1() {
return new Promise(resolve => {
setTimeout(() => {
resolve('p1')
},1000)
})
}
function fn2() {
return new Promise(resolve => {
setTimeout(() => {
resolve('p2')
},2000)
})
}
async function fn() {
let p1 = await fn1()
let p2 = await fn2()
}
fn()
忽略浏览器引擎的耗时,fn1,fn2理想总共耗时3000毫秒
function fn1() {
return new Promise(resolve => {
setTimeout(() => {
resolve('p1')
},1000)
})
}
function fn2() {
return new Promise(resolve => {
setTimeout(() => {
resolve('p2')
},2000)
})
}
async function fn() {
let [res1, res2] = await Promise.all([test1(), test2()])
}
fn()
而这种写发理想耗时2000ms,因为promise.all是将多个异步操作并行实现。
需要注意的是,多个await命令不存在依赖关系。