当咱们在执行异步任务的时候,每每会使用一个回调函数,它会在异步任务结束时被调用,它有一个共同点就是会在将来被执行。例如在node.js中异步读取文件:node
fs.readFile('/etc/passwd', (err, data) => {
if (err) throw err;
console.log(data);
});
复制代码
又如咱们写了个ajax请求函数:ajax
// 简化版,请忽略细节
const request = function(callback){
let xhr = new XMLHttpRequest();
// .....
xhr.onreadystatechange = function(){
callback(xhr)
}
}
//调用
request(function(xhr){
if (xhr.readyState === 4 && xhr.status === 200) {
// ....
} else {
//....
}
})
复制代码
甚至是一个定时器任务:promise
const doSomethingLater = function(delay, callback){
setTimeout(callback, delay*1000)
}
// 调用
doSomethingLater(1, function(){
// .....
})
复制代码
这样看使用回调的方式彷佛没什么问题,可是当回调函数中又有异步任务时,就可能出现多层回调,也就是回调地狱的问题。多层回调下降了代码的可读性和可维护性。bash
简单来说,Promise将异步任务包装成对象,将异步任务完成后要执行的函数传给then方法,经过resolve来调用该函数。如上面定时器任务能够改写成:异步
const doSomethingLater = function(delay){
return new Promise((resolve)=>{
setTimeout(()=>{ resolve() }, delay*1000)
})
}
doSomethingLater(1)
.then(()=>{
console.log('任务1')
})
复制代码
若是定时任务中又执行定时任务,就能够这样写,而不是再嵌套一层回调:async
doSomethingLater(1)
.then(() => {
console.log('任务1')
return doSomethingLater(1)
})
.then(() => {
console.log('任务2')
})
复制代码
Promise的做用:函数
就算你的代码原来没有异步操做:ui
Promise.resolve()
.then(() => {
console.log(1)
})
console.log(2)
// 2
// 1
复制代码
这一点能够查一下事件循环相关知识spa
Promise.reject('error')
.then(() => {
})
.catch((err) => {
console.log(err)
})
// 等价于
Promise.reject('error')
.then(() => {
})
.then(null, (err) => {
console.log(err)
})
// 或者
Promise.reject('error')
.then(() => {
}, (err) => {
console.log(err)
})
复制代码
其实catch只是个语义化的语法糖,咱们也能够直接用then来处理错误。code
then方法第一个参数和第二个参数(或catch的参数),只是调用条件不一样。
Promise.resolve()
.then(() => {
return 1
})
.then((res) => {
console.log(res) // 1
})
Promise.resolve()
.then(() => {
// 不返回什么
})
.then((res) => {
console.log(res) // undefined,由于函数默认返回undefined
})
复制代码
若是是返回一个promise对象:
Promise.resolve()
.then(() => {
return new Promise((resolve) => {
resolve(2)
})
})
.then((res) => {
console.log(res) // 2, 根据返回的那个promise对象的状态来
})
复制代码
咱们能够经过包装,使一个promise对象的最后状态始终是成功的: 例如:
const task = () => {
return new Promise((resolve, reject) => {
// ....
})
}
task()
.then((res) => {
console.log(res)
})
.catch((err) => {
console.log(err)
})
复制代码
本来调用task函数时须要在外面加一层catch捕获错误,其实能够包装一下:
const task = () => {
return new Promise((resolve, reject) => {
// ....
})
.then((res) => {
return {
status: 'success',
value: res
}
})
.catch((err) => {
return {
status: 'fail',
value: err
}
})
}
// 如今调用就会更简洁!
task()
.then((result) => {
console.log(result)
})
复制代码
catch中报错也能够在后面继续捕获,由于catch也是返回promise对象嘛
Promise.reject('first error')
.catch((err) => {
console.log(err)
throw new Error('second error')
})
.then(null, (err) => {
console.log(err)
})
复制代码
await使得异步代码更像是同步代码,对串行的异步调用写起来更天然。await后面跟一个值或promise对象,若是是一个值,那么直接返回。若是是一个promise对象,则接受promise对象成功后的返回值。或者在await后面调用一个async函数
const task = async () => {
return new Promise((resolve, reject) => {
resolve(1)
})
}
const handle = async () => {
let value = await task()
console.log(value)
}
handle() // 1
复制代码
使用await要注意错误的捕获,由于await只关心成功:
const task = async () => {
return new Promise((resolve, reject) => {
reject(1)
})
}
const handle = async () => {
let value = await task()
console.log(value)
}
handle()
复制代码
这样写会报错,因此要么在task函数中捕获错误,要么就在task调用时捕获,像这样:
const task = async () => {
return new Promise((resolve, reject) => {
reject(1)
})
}
const handle = async () => {
let value = await task().catch((err) => { console.log(err) })
console.log(value) // undefine, 由于错误处理函数没返回值,你也能够返回错误信息,这样就会经过value拿到
}
复制代码
须要注意的是,使用await也会使代码变成异步的:
const handle = async () => {
let value = await 1
console.log(value)
}
handle()
console.log(2)
// 2
// 1
复制代码
完~