Promise
为什么会有Promise(Why)
我们都知道js是单线程机制,那么什么是单线程呢?单线程就是按照顺序执行,执行完一个任务之后再执行下一个任务。即同步(Synchronous, sync)编程,同步并不是同时进行,而是在一个控制流序列中按照顺序执行。而异步(Asynchronous, async)编程,是指任务的执行,不再由原有的序列控制。
简单来说,同步为按代码顺序执行,异步为不按代码顺序执行。
那什么时候使用同步,什么时候使用异步呢 ?
一个数据在写入时,有可能被其它线程读取或者被写,这个时候可以使用同步编程;那么如果一个数据在子线程中不被主线程使用或者不被其它子线程使用,就可以使用异步编程,提高效率。
但是,使用异步编程相较于同步编程会有一个明显的问题,那就是同步编程只有一个主线程,我们可以知道任务什么时候开始,什么时候结束,在什么状态,而异步编程的子线程独立于主线程,子线程一旦发射便不再与主线程同步,将无法确定子线程的结束,若需要在子线程结束后处理数据,将没有办法将数据合并到主线程,为了解决这一问题,JavaScript 中的异步操作通常通过回调函数来实现异步编程的结果处理。
回调函数:
setTimeout(() => {
console.log('456')
}, 1000);
console.log('123');
这便是一个典型的异步回调函数案例,‘123’先执行,setTimeout子线程等待1s,后回调函数返回’456’。回调函数确实可以解决异步编程子线程不同步主线程的问题,但如果嵌套过多,易造成回调地狱,不利于代码的编写和阅读。
后ES6推出Promise,来解决异步编程的问题,比起传统的回调函数来说,Promise更强大且优雅。
Promise是什么(What)
Promise是ES6提供的类,为了更直观的看懂Promise,将此打印出来。
可以清晰的Promise就是一个构造函数,自己身上有all、reject、resolve几个方法,原型上有then及catch等方法,那么我们可以new出来一个对象:
new Promise(function (resolve, reject) {
// 要做的事情...
});
Promise的构造函数接受一个参数,这个参数是一个函数,该函数就是Promise构造函数的回调函数,此函数又有两个参数,一个resolve,一个reject,分别表示成功的回调和失败的回调,即为fullfiled和rejected。
这么看其实看不出来Promise有多么优雅和强大,那么我们用以下例子来说明一下:
分三次输出成绩,第一次间隔1s,第二次间隔5s,第三次间隔10s,用回调函数书写:
setTimeout(() => {
console.log("60分");
setTimeout(() => {
console.log("80分");
setTimeout(() => {
console.log("100分");
}, 10000);
}, 5000);
}, 1000);
无论是代码样式还是维护,都会显得很繁琐,那么我们用Promise来试一下:
new Promise((resolve, reject) => {
setTimeout(function () {
console.log("60分");
resolve();
}, 1000);
}).then(() => {
return new Promise(function (resolve, reject) {
setTimeout(() => {
console.log("80分");
resolve();
}, 5000);
});
}).then(() => {
setTimeout(() => {
console.log("100分");
}, 10000);
});
很明显我们发现,由原来的不是顺序代码变得有顺序了起来,更为优雅更好维护了。
怎么使用Promise(Where)
Promise 的方法
- then()
- catch()
- all()
- race()
- finally()
let funPromise = new Promise((resolve, reject) => {
// 做异步操作;
resolve('success');
});
funPromise.then((res) => {
console.log('res', res);
});
then()
then() 接受两个回调函数作为自己的参数,当Promise执行的内容符合成功条件时,调用resolve函数;第二个参数可以省略。且then方法返回的时一个新的Promise实例,可以链式调用,即如下:
funPromise.then((res) => {
console.log('res', res);
}).then((res2) => {
console.log('res2', res2);
});
catch()
catch()方法是then方法的第二个参数,指向reject的回调函数;若执行resolve回调函数时,如果出现错误的话,也会进入到catch方法中。
funPromise.then((success) => {
console.log('resolved', data);
}, (error) => {
console.log('rejected', error);
}
);
all()
all()方法,顾名思义,即为全部所有任务。all方法可以并行完成任务,接收一每一项都是promise对象的数组,当使用all方法时,数组的每一项promise的状态都达到resolved的时候,all方法的状态就会变成resolved,返回值为每一项的返回值组成的数组。假如有一个不是resolved,那么all的状态就是rejected。
let promise1 = new Promise((resolve,reject)=>{
setTimeout(() = >{
resolve(1);
}, 2000)
});
let promise2 = new Promise((resolve,reject)=>{
setTimeout(() = >{
resolve(2);
}, 1000)
});
Promise.all([promise1,promise2]).then(res=>{
console.log(res); // [1,2]
})
race()
race,比赛的意思,与all方法类似,接收的也是每一项都是promise的数组,最先执行完的执行完之后,就直接返回,
const p = Promise.race([p1, p2, p3]);
p1,p2,p3只要有一个先执行完,那么p的状态就会随之改变,其余的就不会再返回了。
finally()
finally方法,书写习惯是写在then及catch之后,意思是在这个Promise执行的最后一定会执行的序列。
promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});
总结
当然,Promise不止这几个方法,还有resolve, reject 等方法,最常用的还是上面的几种方法。
今天的分享就这样啦,感谢观看。一起成长!