Promise是异步操作的一种解决方案
// 1.认识Promise
document.addEventListener('click',()=>{
console.log('这里是异步的');
});
console.log('这里是同步的');
Promise一般用来解决层层嵌套的回调函数(回调地狱)的问题
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<style>
*{
padding: 0;
margin: 0;
}
#box{
width: 300px;
height: 300px;
background-color: red;
transition: all .5s;
}
</style>
</head>
<body>
<div id="box"></div>
<script>
// 运动
const move=(el,{x=0,y=0}={},end=()=>{})=>{
el.style.transform=`translate3d(${x}px,${y}px,0`;
el.addEventListener('transitionend',()=>{
end();
});
};
const boxEl=document.getElementById('box');
document.addEventListener('click',()=>{
move(boxEl,{x:150},()=>{
move(boxEl,{x:150,y:150},()=>{
move(boxEl,{y:150},()=>{
move(boxEl,{x:0,y:0});
})
})
})
});
</script>
</body>
</html>
Promise的基本用法
- 实例化构造函数生成实例对象
- Promise的状态
// 1.实例化构造函数生成实例对象
console.log(Promise);
// Promise解决的不是回调函数,而是回调地狱
//const p=new Promise(()=>{});
// 2.Promise的状态
const p=new Promise((resolve,reject)=>{
});
console.log(p);
Promise有3种状态,一开始是pending(未完成),执行resolve,变成fulfilled(resolved),已成功,执行reject,变成rejected,已失败
// 2.Promise的状态
const p=new Promise((resolve,reject)=>{
resolve();
});
console.log(p);
// 2.Promise的状态
const p=new Promise((resolve,reject)=>{
// resolve();
reject();
});
console.log(p);
Promise的状态一旦发生变化,就不会再变化了 eg:先调用resolve()再调用reject() 状态从pending->fulfilled 之后虽然执行了reject(),但是状态不变还是fulfilled
// 2.Promise的状态
const p=new Promise((resolve,reject)=>{
resolve();
reject();
});
console.log(p);
- then方法
// 2.Promise的状态
const p=new Promise((resolve,reject)=>{
resolve();
});
// 3.then()方法
// 第一个回调函数代表成功 第二个回调函数代表失败
p.then(()=>{
console.log('success');
},()=>{
console.log('error');
});
控制台输出success,如果调用reject()方法,则输出error
- resolve和reject函数的参数
// 2.Promise的状态
const p=new Promise((resolve,reject)=>{
resolve({username:'jessica'});
//reject(new Error('reason'));
});
// 3.then()方法
// 第一个回调函数代表成功 第二个回调函数代表失败
// 4.resolve()和reject()参数
p.then((data)=>{
console.log('success',data);
},(err)=>{
console.log('error',err);
});
Promise的实例方法
then()返回的是一个新的Promise对象
- 什么时候执行
- 执行后的返回值
- then方法返回的Promise对象的状态改变(return后面的东西会用Promise包装一下)
const p=new Promise((resolve,reject)=>{
// 第一次调用resolve方法必须写 后面新的Promise对象可不写
resolve();
});
p.then(()=>{
console.log('success1');
// 默认返回的是 undefined
// 默认返回的是成功状态的Promise对象
// 不传参数的话可以不写return语句 最方便
// return undefined;
// 传参数的话直接return+参数 方便
// return 123;
// 也可以用下面的形式 源代码就是这样
return new Promise((resolve,reject)=>{
// resolve(undefined);
// resolve(123);
reject('reason');
})
},()=>{
console.log('error1');
}).then(data=>{
console.log('success2',data);
},err=>{
console.log('error2',err);
})
- 向后传值
- 使用Promise解决回调地狱
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<style>
* {
padding: 0;
margin: 0;
}
#box {
width: 300px;
height: 300px;
background-color: red;
transition: all .5s;
}
</style>
</head>
<body>
<div id="box"></div>
<script>
// 运动
const move = (el, {x = 0, y = 0} = {}, end = () => {
}) => {
el.style.transform = `translate3d(${x}px,${y}px,0`;
el.addEventListener('transitionend', () => {
end();
});
};
const boxEl = document.getElementById('box');
const movePromise = (el, point) => {
return new Promise(resolve => {
move(el, point, () => {
resolve();
});
});
};
document.addEventListener('click', () => {
movePromise(boxEl, {x: 150})
.then(() => {
return movePromise(boxEl, {
x: 150, y: 150
});
})
.then(() => {
return movePromise(boxEl, {
y: 150
});
})
.then(() => {
return movePromise(boxEl, {x: 0, y: 0});
});
}, false);
</script>
</body>
</html>
catch()方法专门用来处理rejected状态
catch()可以捕获它前面的错误,一般总是建议,Promise对象后面要跟catch方法,这样可以处理Promise内部发生的错误
const p=new Promise((resolve,reject)=>{
//resolve();
reject('reason');
});
// 第一个then只有一个箭头函数,但是由于上面是失败状态
// 只能通过下一次then继续寻找第二个箭头函数,如若找不到以此类推寻找
// p.then(data=>{
// console.log(data);
// }).then(null,err=>{
// console.log(err);
// });
// catch是then的特例
// 专门捕获失败时状态
p.then(data=>{
console.log(data);
}).catch(err=>{
console.log(err);
//默认返回undefined,成功状态的Promise对象
// 可以制造出失败时错误
throw new Error('reason');
}).then(data=>{
console.log(data);
})
finally()方法
- 什么时候执行
- 当Promise状态发生变化时,不论如何变化都会执行,不变化不执行
// finally一般不接收参数
new Promise((resolve,reject)=>{
// resolve(123);
reject('reason');
}).finally(data=>{
console.log(data);
}).catch(err=>{
console.log(err);
})
构造函数方法
Promise.resolve()和Promise.reject()
当Promise.resolve()的参数接收的是Promise对象时,那么直接返回Promise对象,什么都不做
const p1=new Promise(resolve => {
setTimeout(resolve,1000,'我执行了');
// 定时器第三个参数相当于以下这种形式
setTimeout(()=>{
resolve('我执行了')
},1000);
});
// 当Promise.resolve()接收的是Promise对象时,
// 直接返回这个Promise对象,什么都不做
Promise.resolve(p1).then(data=>{
console.log(data);
})
// 等价于
// p1.then(data=>{
// console.log(data);
// })
console.log(Promise.resolve(p1) === p1);//true
控制台隔了1s后才输出我执行了
当resolve函数接收的是Promise对象时,后面的then会根据传递的Promise对象的状态变化决定执行哪一个回调
const p1 = new Promise(resolve => {
setTimeout(resolve, 1000, '我执行了');
// 定时器第三个参数相当于以下这种形式
setTimeout(() => {
resolve('我执行了')
}, 1000);
});
new Promise(resolve => resolve(p1)).then(data=>{
console.log(data);
})
当resolve函数接收的是具有then()方法的对象时
const thenable={
then(){
console.log('then');
}
};
Promise.resolve(thenable).then(
data=>console.log(data),
err=>console.log(err));
console.log(Promise.resolve(thenable));
pending状态,所以回调一个都不会执行
要执行回调,需要以下操作
const thenable={
then(resolve,reject){
console.log('then');
resolve('resolve');
}
};
Promise.resolve(thenable).then(
data=>console.log(data),
err=>console.log(err));
console.log(Promise.resolve(thenable));
reject函数:不管什么参数,都会原封不动地向后传递作为后续方法的参数
const p1 = new Promise(resolve => {
setTimeout(resolve, 1000, '我执行了');
// 定时器第三个参数相当于以下这种形式
// setTimeout(() => {
// resolve('我执行了')
// }, 1000);
});
// new Promise((resolve,reject)=>{
// reject('reason');
// })
// // 简写形式
// Promise.reject('reason');
Promise.reject(p1).catch(
err => console.log(err)
)
Promise.all()
- 关注多个Promise对象的状态变化 传入多个Promise实例,包装成一个新的Promise实例返回
- Promise.all()的状态变化与所有传入的Promise实例对象状态有关
- 所有状态都变成resolved,最终的状态才会变成resolved
- 只要有一个变成rejected,最终的状态就变成rejected
const delay=ms=>{
return new Promise(resolve => {
setTimeout(resolve,ms);
})
};
const p1=delay(1000).then(()=>{
console.log('p1 完成了');
return 'p1';
});
const p2=delay(2000).then(()=>{
console.log('p2 完成了');
return 'p2';
// return Promise.reject('reason');
});
const p=Promise.all([p1,p2]);
p.then(data=>{
console.log(data);
},err=>{
console.log(err);
})
Promise.race():竞赛永争第一
- Promise.race()的状态取决于第一个完成的Promise实例对象,如果第一个完成的成功了,那最终就成功;如果第一个完成的失败了,那最终就失败
const delay=ms=>{
return new Promise(resolve => {
setTimeout(resolve,ms);
})
};
const p1=delay(1000).then(()=>{
console.log('p1 完成了');
// return 'p1';
return Promise.reject('reason');
});
const p2=delay(2000).then(()=>{
console.log('p2 完成了');
return 'p2';
});
const racePromise=Promise.race([p1,p2]);
racePromise.then(data=>{
console.log(data);
},err=>{
console.log(err);
})
Promise.allSettled()
- Promise.allSettled()的状态与传入的Promise状态无关,永远都是成功的
const delay=ms=>{
return new Promise(resolve => {
setTimeout(resolve,ms);
})
};
const p1=delay(1000).then(()=>{
console.log('p1 完成了');
return 'p1';
//return Promise.reject('reason');
});
const p2=delay(2000).then(()=>{
console.log('p2 完成了');
return 'p2';
});
const allSettledPromise=Promise.allSettled([p1,p2]);
allSettledPromise.then(data=>{
console.log(data);
});
Promise的注意事项
- resolve或reject函数执行后的代码(推荐在调用其函数时加上return,不再执行它们后面的代码)
- Promise.all/race/allSettled的参数问题
- 参数如果不是Promise数组,会将不是Promise的数组元素转变成Promise对象
Promise.all([1,2,3]).then(datas=>{
console.log(datas);
})
// 相当于
Promise.all([
Promise.resolve(1),
Promise.resolve(2),
Promise.resolve(3)]
).then(datas=>{
console.log(datas);
})
- 不只是数组,任何可遍历的都可以作为参数(数组、字符串、Set、Map、NodeList、arguments)
Promise.all(new Set([1,2,3])).then(datas=>{
console.log(datas);
})
- Promise.all/race/allSettled的错误处理
Promise的应用
异步加载图片
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<style>
#img {
width: 80%;
padding: 10%;
}
</style>
</head>
<body>
<img src="https://img.mukewang.com/5e6af63d00011da318720764.jpg" alt="" id="img" >
<script>
// 异步加载图片
const loadImgAsync = url => {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => {
resolve(img);
}
img.onerror = () => {
reject(new Error(`Could not load image at ${url}`));
}
img.src = url;
});
};
const imgDOM = document.getElementById('img');
loadImgAsync('https://2img.mukewang.com/5f057a6a0001f4f918720764.jpg')
.then(img => {
console.log(img.src);
setTimeout(() => {
imgDOM.src = img.src;
}, 1000);
})
.catch(err => {
console.log(err);
});
</script>
</body>
</html>