Promise 是什么?
mdn:Promise 对象用于表示一个异步操作的最终完成 (或失败)及其结果值。
既然Promise是为了表示异步的最终结果,那异步是什么呢?为啥要用Promise来表示异步的结果?而不用其他方式呢?
什么是异步
用一个例子来说明
当你在餐厅吃饭,首先你得先去排队拿号,
但是,你前面还有10桌需要等待,可能需要几十分钟,
如果你在这里傻等,那就是同步操作,
但正常人没那么傻,大部分人都是先拿票,然后去点其他事情,这就是异步操作
那如何知道是否轮到自己?
- 隔一会去拿票的地方问(轮询)
- 用微信小程序接收通知(回调)
异步即立即拿不到结果需要通过回调和轮询来获取异步操作的结果
下面是一些异步操作的典型案例
- setTimeout
- AJAX
- AddEventListener
获取异步的结果
- 回调callback
用微信小程序接收通知
function 排队(号码, 到我了吗){
if(号码轮到我了){
return 到我了吗('快来吃饭啦');
}
}
function 到我了吗(到我了吗){
return '快来吃饭啦';
}
- 轮询
隔一会去拿票的地方问
//设置定时器,每隔20分钟去问一次
const 排队吃饭 = setInterval(() => {
if (结果 === '到我啦') {
//到我了就去吃饭,清除计时器,停止轮询
clearInterval(this.timeId)
};
问前台轮到我了吗();
}, 1000*60*20)
为啥要使用Promise
既然轮询和回调都可以拿到结果,为啥要用promise呢
异步的结果可能会有两种,一是成功,二是失败,所以我们要么使用两个回调,要么回调接收两个参数
//回调接受两个参数
fs.readFile('./1.txt', (error, data) => {
if (error) {
console.log('失败');//失败返回失败信息
return
}
console.log(data.toString())//成功返回数据结果
})
//用两个回调来接受结果
ajax('get','/1.json', data=>{}, error=>{})
//回调地狱(仅4层的回调?如果更多呢?)
getUser(user => {
getGroups(user, (groups) => {
groups.forEach((g) => {
g.filter(x => x.ownerId === user.id)
.forEach(x => console.log(x))
})
})
})
缺点:
- 回调参数的命名没有明确规定
- 回调一多,容易出现回调地狱
- 很难进行错误处理
Promise
前面两种方法都有一些缺陷,而Promise的设计思想刚好又解决了这些缺陷
Promise的原理:使用resolve, reject两个函数控制Promise内部成功和失败状态,promise.then()根据成功和失败执行不同的函数,实现异步
- 创建Promise对象
new Promise((resolve,reject)=>{})
- Promise.prototype.then
//通过then函数传入成功和失败的执行操作,success不可省略,fail可省略
new Promise((resolve,reject)=>{}).then(success,fail)
- Promise.all
将多个Promise实例包装成一个新的Promise实例,实例全部成功则返回数组,实例失败则返回第一个失败的状态值
Promise.all可以let obj = new Promise((resolve,reject)=>{resolve('成功')})
let obj1 = new Promise((resolve,reject)=>{resolve('成功')})
let obj2 = new Promise((resolve,reject)=>{reject('失败')})
//执行结果["成功", "成功"]
Promise.all([obj,obj1]).then((result) => {
console.log(result)
}).catch((error) => {
console.log(error)
})
//执行结果 失败
Promise.all([obj,obj2]).then((result) => {
console.log(result)
}).catch((error) => {
console.log(error)
})
- promise.race,获取多个实例中执行最快的那个实例的返回结果(不管处于成功状态和失败状态)
let obj = new Promise((resolve,reject)=>{
setTimeout(() => {
resolve('成功')
}, 500)
})
let obj1 = new Promise((resolve,reject)=>{
setTimeout(() => {
reject('失败')
}, 1000)
})
//由于obj的执行时间较快,执行结果成功
Promise.race([obj,obj1]).then((result) => {
console.log(result)
}).catch((error) => {
console.log(error)
})
手写简易版Promise
实现思路:Promise内部定义两个队列,用于存放用户传过来的成功或失败之后的函数,提供resolve和reject用于成功和失败之后执行队列中的函数
class Promise2{
//创建两个队列用于存放成功或失败后要执行的函数
queue1 = []
queue2 = []
constructor(fn){
//成功函数
const resolve = (data) =>{
//执行队列中的函数
setTimeout(()=>{
for(let i =0;i< this.queue1.length;i++){
this.queue1[i](data)
}
})
}
//失败函数
const reject = (reason) =>{
setTimeout(()=>{
for(let i = 0; i< this.queue2.length;i++){
this.queue2[i](reason)
}
})
}
fn(resolve,reject);
}
then(s,e){
this.queue1.push(s);
this.queue2.push(e);
return this
}
}
var p1 = new Promise2((resolve,reject)=>{console.log('hi');resolve();})
p1.then(()=>{ console.log('成功')})