为什么要用promise?
说到promise就绕不开同步和异步的问题,那么什么是同步什么是异步?
同步就是比如你烧了一壶水,但是此时你想看电视,但是你只能等这壶水烧开了,你才能去看电视,也就是事件是一件完成以后才能去做下一件,这个是同步。
异步就是我再烧水的同时,我也去看电视,当水烧开后发出声音,此时这个声音就相当于个回调函数,告诉我们这个水烧开了,事情做完了,然后我们再去看电视,这个就是异步。
但是当我们异步请求,假如一个需求要请求3个接口,第二个接口参数依赖于第一个的返回值,第三个接口的参数依赖于第二个接口的返回值,那么我们看看常用的ajax异步调用的例子,会出现如下情况:
$.ajax({
......
success(data) {
$.ajax({
success(data2) {
$.ajax({....})
}
})
}
})
我们会发现,异步处理全部是以回调函数的方式进行的,而且层次感不强,也就是所谓了回调地狱,假如再来个5个回调6个回调,此时就不好维护了,而且剥夺了函数的return的能力,你return了,之后的就不会调用了,所以promise出现就是为了解决这个问题的。
文章目录
promise的用法
promise有三种状态:
- pending: 初始状态,不是成功或失败状态。
- fulfilled: 意味着操作成功完成。
- rejected: 意味着操作失败。
这三种状态是不可逆的,而且只能是从等待到成功或者等待到失败,成功了失败是无法相互转换的,同样成功或者失败态也无法到等待态。promise 原生js提供一个类,new Promise() 需要传递一个executor 执行函数,该函数立即执行,默认是等待态调用resolve 为成功态,reject为失败态,then是异步执行。
let p = new Promise((resolve, reject) => {
console.log("ok");
// resolve("成功了");
reject("失败了");
});
p.then((data) => {
console.log(data);
}, (error) => {
console.log(error); // 失败了
})
let p2 = new Promise((resolve, reject) => {
console.log("ok");
resolve("成功了");
reject("失败了");
});
p2.then((data) => {
console.log(data); // 成功了
}, (error) => {
console.log(error); // 不会执行
})
也就是resolve和reject只会执行一个,谁先执行,之后的就不会执行,也就是上面所说的状态的不可逆。
案例:使用promise处理异步和不使用的对比
let fs = require("fs")
// 使用回调
fs.readFile('./file1',"utf-8", function (err, data) {
console.log(data)
fs.readFile('./file2',"utf-8", function (err, data) {
console.log(data)
})
})
// 使用promise
// 定义读取文件的函数
function readFile(url) {
// 返回一个promise实例
return new Promise((resolve, reject) => {
fs.readFile(url, "utf8", function (err, data) {
if (err) {
reject(err);
} else {
resolve(data);
}
})
});
}
readFile('./file1').then((data) => {
return readFile('./file22')
}then((data) => {
console.log('data', data)
}, (err) => {
console.log('err', err)
})
这样看来使用promise可以很清晰的看到接口的执行顺序,层次感很强。
一、链式调用返回值的问题
我们都知道promise支持链式调用是因为then之后会返回一个新的promise对象,那么当返回其他值的时候呢?
情况1:当then方法返回一个普通值(数字,字符串,对象)的时候:
let p = new Promise((resolve, reject) => {
resolve("成功了");
});
p.then((data) => {
console.log(data);
return 123;
}, (error) => {
console.log(error); // 失败了
}).then((data) => {
console.log(data); // 123
},(err) => {
console.log('err2', err)
}).then((data) => {
console.log('data2',data); // 打印 undefined
},(err) => {
console.log('err3', err)
})
所以当返回一个普通值的时候,这个值会被传递到下一个then的成功函数中。
情况2:假如返回的不是一个普通值,而是抛出个异常呢?
let p = new Promise((resolve, reject) => {
resolve("成功了");
});
p.then((data) => {
console.log(data);
throw new Error("报错了 无情啊");
}, (error) => {
console.log(error); // 失败了
}).then((data) => {
console.log(data); // 123
},(err) => {
console.log('err2', err) // 打印错误
}).then((data) => {
console.log('data2',data); // 打印 undefined
},(err) => {
console.log('err3', err)
})
所以当扔出一个报错的时候,这个值会被传递到下一个then的失败函数中。
情况3:
let p = new Promise((resolve, reject) => {
resolve("成功了");
});
p.then((data) => {
console.log(data); // 打印成功了
return new Promise((resolve, reject) => {
resolve("成功了,success");
// reject("失败了,error")
})
}, (error) => {
console.log(error); // 如果上一个then执行reject那么这个打印失败了,error
}).then((data) => {
console.log(data); // 成功了,success
},(err) => {
console.log('err2', err)
}).then((data) => {
console.log('data2',data); // 打印 undefined
},(err) => {
console.log('err3', err)
})
所以返回的如果是一个promise 会根据返回的promise是成功还是失败,然后决定返回到成功还是失败,也就是说链式调用,后一个then要看前一个then的返回值,不论它是执行了成功还是失败。
二、错误捕获机制
根据上面的情况2可以知道,如果是抛出一个异常那么会传递到下一个失败中去捕获,那么catch捕获呢?
let p = new Promise((resolve, reject) => {
resolve("成功了");
});
// catch就近
p.then((data) => {
console.log(data);
throw new Error('error')
}, (error) => {
console.log(error); // 失败了
}).catch(err2 => {
console.log('catch', err2) // 捕获异常
}).then((data)=> {
},(err3) => {
console.log('err3', err3)
})
// 失败就近
p.then((data) => {
console.log(data);
throw new Error('error')
}, (error) => {
console.log(error); // 失败了
}).then((data)=> {
},(err3) => {
console.log('err3', err3) // 捕获异常
}).catch(err2 => {
console.log('catch', err2)
})
所以错误的捕获机制 是找最近的then的失败,以此往下,如果最近有失败捕获,那么catch不会捕获,否则找catch捕获。
三.穿透功能
let p = new Promise((resolve, reject) => {
resolve("7777!");
});
// 穿透功能(原生)
p.then().then().then((data) => {
console.log(data) // 打印7777
},(error)=>{
console.log(error)
})
多个串联then执行,会穿透到最后一个,根据是执行成功或者失败来决定。
四.promise的方法
1.all()
// 定义一个读取文件的方法
function readFile(url) {
// 返回一个promise实例
return new Promise((resolve, reject) => {
fs.readFile(url, "utf8", function(err, data) {
if (err) {
reject(err);
} else {
resolve(data);
}
})
});
}
Promise.all([readFile('./file1'),
readFile('./file2')]).then((data) => {
console.log(data)
}, (err) => {
console.log(err)
})
把所有promise实例成功时传都参数 保存在then的成功回调里面,all中只要有一个失败的promise就会执行then的失败回调。
2 race()
Promise.race([readFile('./file1'),
readFile('./file2')]).then((data) => {
console.log(data)
}, (err) => {
console.log(err)
})
谁先回来的状态决定then调用成功还是失败,常量最先回来。
3 reject()
Promise.reject(1111).then((data) => {
console.log('hello world')
}, (err) => {
console.log(err) // 打印111
})
4 resolve()
返回一个以给定值解析后的Promise 对象
Promise.resolve(123).then((data) => {
console.log(data) // 打印123
}, (err) => {
console.log(err)
})
5 finally()
Promise.resolve(123).then((data) => {
console.log(data) // 打印123
}, (err) => {
console.log(err)
}).finally(() => {
console.log("完成了")
})
不管promise最后的状态,在执行完then或catch指定的回调函数以后,都会执行finally方法指定的回调函数。