一、什么是Promise
1.Promise有什么
先打印到控制台上
console.dir(Promise)
可以看到原型有构造函数、catch、then、finally、Symbol(ES6原始数据类型,解决命名冲突问题),本身有all、reject、resolve、race函数,与其他构造函数没什么大的区别
new个实例看看
var p = new Promise((resolve, reject) => {
// 模拟调用接口
setTimeout(() => {
console.log('调用接口');
resolve('返回数据')
}, 2000)
})
看到Promise需要传入resolve,reject两个回调函数,用于执行执行成功和执行失败两种情况
二、Promise可以做什么
1、Promise的异步操作
Promise可以用来执行异步操作,这是Promise最常见的用法,比如上面的setTimeout,在实际的开发中可以替换为下发到后台的接口,调用接口成功回调resolve,调用接口失败回调reject
2、Promise的链式调用
原型中有then,catch这些函数,所以可以执行链式调用
举个栗子
var p = new Promise((resolve, reject) => {
// 模拟调用接口
setTimeout(() => {
console.log('调用接口');
resolve('返回数据')
}, 2000)
}).then(() => {
console.log('调用接口后');
}).finally(() => {
console.log('调用接口完成');
})
3、Promise简化回调
先看下面的代码
function runAsync(resolve) {
// 模拟调用接口
setTimeout(() => {
console.log('调用接口');
resolve('返回数据')
}, 2000)
}
runAsync((data) => {
console.log(data);
})
我写了一个回调函数,作为参数传入runAsync,等接口调用完成后执行,不也可以实现回调?感觉还更简洁,那Promise有什么好?
但其实这只是一种特殊情况,如果callback本身是一个异步操作呢?还要给callback传一个callback2?这样一点也不优雅,代码层层回调也很容易让人看不懂,而Promise的优势在于,可以在then方法中继续写Promise对象并返回,然后继续调用then来进行回调操作
像是这样
function runAsync1() {
var p = new Promise((resolve, reject) => {
// 模拟调用接口
setTimeout(() => {
console.log('调用接口1');
resolve('返回数据')
}, 2000)
})
// 返回的是Promise对象
return p
}
function runAsync2() {
var p = new Promise((resolve, reject) => {
// 模拟调用接口
setTimeout(() => {
console.log('调用接口2');
resolve('返回数据')
}, 2000)
})
// 返回的是Promise对象
return p
}
function runAsync3() {
var p = new Promise((resolve, reject) => {
// 模拟调用接口
setTimeout(() => {
console.log('调用接口3');
resolve('返回数据')
}, 2000)
})
// 返回的是Promise对象
return p
}
runAsync1()
.then((data) => {
console.log(data);
return runAsync2()
})
.then((data) => {
console.log(data);
return runAsync3()
})
.then(function (data) {
console.log(data);
});
这样接口调用能够老老实实地按接口调用的顺序返回,代码更简洁,代码维护起来更方便
4、reject和catch
上面说了resolve,现在讲reject,then函数可以传入两个回调函数,像这样
getNum().then(
(data) => {
console.log(data);
console.log(aaa);
},
(data) => {
console.log(data);
}
)
和resolve用法一样,不多赘述
catch的用法是作为链式操作,和then的第二个参数reject作用一样
function getNum() {
var p = new Promise((resolve, reject) => {
const num = Math.ceil(Math.random() * 10)
if (num <= 5) {
resolve('数字=' + num)
} else {
reject('数字=' + num + '数字太大了')
}
})
return p
}
getNum().then(
(data) => {
console.log(data);
}
// (data) => {
// console.log(data);
// }
)
.catch((data) => {
console.log(data);
})
但catch还有另一个用法,就是当代码出错时捕获,reject无法做到
getNum().then(
(data) => {
console.log(data);
console.log(aaa);
}
// (data) => {
// console.log(data);
// }
)
.catch((error) => {
console.log(error.message);
})
这里提一个小建议:
能够预判到的异常,如数字超出范围的,用reject,不能预判的异常,如代码漏洞,接口报400的,用catch
finally函数不多讲,也提一个小建议:
finally执行resolve和reject都要执行的操作,如数据的赋值,接口返回的标志
三、Promise的常用函数
上面说的都是比较常见的函数,下面说下Promise的独有的方法
1、Promise.race()
race,意为赛跑,把多个Promise的实例做列表,作为参数传入,只要有一个执行完成,就返回
举个栗子
var p1 = new Promise((resolve, reject) => {
// 模拟调用接口
setTimeout(() => {
console.log('调用接口1');
resolve('返回数据1')
}, Math.random() * 1000)
})
var p2 = new Promise((resolve, reject) => {
// 模拟调用接口
setTimeout(() => {
console.log('调用接口2');
resolve('返回数据2')
}, Math.random() * 1000)
})
var p3 = new Promise((resolve, reject) => {
// 模拟调用接口
setTimeout(() => {
console.log('调用接口3');
resolve('返回数据3')
}, Math.random() * 1000)
})
Promise.race([p1, p2, p3])
.then((data) => {
console.log(data);
})
这里用random模拟接口返回的快慢,then函数resolve的参数只有一个,只处理第一个返回的数据,但其他异步操作并没有中断,而是继续执行
2、Promise.all()
和race不同,all()需要所有异步操作全部成功完成,才执行resolve, 只要有一个执行失败就会执行reject
resolve返回的也不一样,是每个异步操作调用resolve的结果
var p1 = new Promise((resolve, reject) => {
// 模拟调用接口
setTimeout(() => {
console.log('调用接口1');
resolve('返回数据1')
}, Math.random() * 1000)
})
var p2 = new Promise((resolve, reject) => {
// 模拟调用接口
setTimeout(() => {
console.log('调用接口2');
resolve('返回数据2')
}, Math.random() * 1000)
})
var p3 = new Promise((resolve, reject) => {
// 模拟调用接口
setTimeout(() => {
console.log('调用接口3');
resolve('返回数据3')
}, Math.random() * 1000)
})
Promise.all([p1, p2, p3])
.then((data) => {
console.log(data);
})
3、race和all的用法
当有多个异步操作,但只需要一个接口返回时,可以用race,比如请求一个资源,后台执行时间不确定时,当第一次返回,直接用第一次返回的数据,多次请求没有返回的话,报资源请求超时错误抛出
当有多个异步操作。但需要全部接口返回时,可以用all,比如请求一个大规模的数据,一个接口携带的数据量不够,可以分批多次下发,接口全部返回才展示到界面上
四、一些需要注意的点
后面想到再补充