一、什么是Promise?
Promise 是异步编程的一种解决方案:从语法上讲,promise是一个对象(Promise对象是一个构造函数,用来生成Promise实例),从它可以获取异步操作的消息;从本意上讲,它是承诺,承诺它过一段时间会给你一个结果。
Promise 不解决异步问题,解决的是异步的写法
二、语法
new Promise( function(resolve, reject) {...} /* executor */ );
let p = new Promise(function(resolve,reject){
// 这个函数会被 Promise
setTimeout(()=>{
resolve("传参");// 当我们调用了 resolve 就代表该异步完成,并且成功拿到结果
},2000);
});
p.then(function(res){
console.log(res);//传参
});
三、参数 executor
executor是带有 resolve 和 reject 两个参数的函数 。
Promise构造函数执行时立即调用executor 函数, resolve 和 reject 两个函数作为参数传递给executor(executor 函数在Promise构造函数返回新建对象前被调用)。
resolve 和 reject 函数被调用时,分别将promise的状态改为fulfilled(完成)或rejected(失败)。
executor 内部通常会执行一些异步操作,一旦完成,可以调用resolve函数来将promise状态改成fulfilled,或者在发生错误时将它的状态改为rejected。
如果在executor函数中抛出一个错误,那么该promise 状态为rejected。executor函数的返回值被忽略
let p = new Promise(function(resolve,reject){
let img = new Image();
img.src = `https://timgsa.babidu.com/timg?image&quality=80&size=b9999_10000&sec=1576855691894&di=b694b104df1eb0b53ee6b52d8ad6fb49&imgtype=0&src=http%3A%2F%2Fpic.vjshi.com%2F2018-10-30%2F26b945dcada739a37b4d1fecc1d10abb%2F00004.jpg%3Fx-oss-process%3Dstyle%2Fwatermark`;
img.onload = function(){
resolve("图片请求成功")
};
img.onerror = function(){
reject("图片请求失败");
};
}).then((res)=>{
console.log(res);
},(rej)=>{
console.log(rej);
});
四、then
promise.then(onFulfilled,onRejected)
参数:
- onFulfilled
当Promise变成接受状态(fulfillment)时,该参数作为回调函数被调用(参考: Function)。该函数有一个参数,即接受的最终结果(the fulfillment value)。如果传入的 onFulfilled 参数类型不是函数,则会在内部被替换为(x) => x ,即原样返回 promise 最终结果的函数 - onRejected
当Promise变成拒绝状态(rejection )时,该参数作为回调函数被调用(参考: Function)。该函数有一个参数,,即拒绝的原因(the rejection reason)。
new Promise(promise的回调函数,在实例化时会直接执行function(resolve,reject){
// resolve 调用该 函数 resolve(传递给成功的执行函数的数据) 代表异步执行完成,
并成功拿到结果
// reject 调用该函数 reject(传递给处理失败的执行函数的数据) 代表异步执行完成,
但是没有成功拿到结果
}).then((成功之后传递过来的数据)=>{
console.log("成功之后的业务处理");
},(失败之后传递过来的数据)=>{
console.log("失败之后的业务处理");
});
Promise 的then方法会返回一个新的Promise对象
五、描述
Promise 对象是一个代理对象(代理一个值),被代理的值在Promise对象创建时可能是未知的。它允许你为异步操作的成功和失败分别绑定相应的处理方法(handlers)。 这让异步方法可以像同步方法那样返回值,但并不是立即返回最终执行结果,而是一个能代表未来出现的结果的promise对象一个 Promise有以下几种状态:
- pending: 初始状态,既不是成功,也不是失败状态。
- fulfilled: 意味着操作成功完成。
- rejected: 意味着操作失败。
六、 链式操作的用法
Promise 的 then 方法,会给我们返回一个新的 Promise 对象
新的 Promise 对象的执行状态:
- 默认情况下: then 返回的是一个状态是 resolved 的 Promise 对象
- 当then的回调函数返回的是一个 非 Promise 对象,then 返回的是一个状态是 resolved 的 Promise 对象
- 当 then 的回调函数返回的是一个 Promise 对象,then 返回值,也会变成该 Promise 对象
let p = new Promise((resolve,reject)=>{
resolve(1);
});
let p2 = p.then((data)=>{
console.log("resolve");
return new Promise((resolve,reject)=>{
//reject("1");
});
},(data)=>{
console.log("reject");
});
p2.then((data)=>{
console.log("p2的resolve");
},(data)=>{
console.log("p2的reject");
});
基于链式操作的demo示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
#box {
position: absolute;
left: 0;
top: 0;
width: 100px;
height: 100px;
background: red;
}
</style>
</head>
<body>
<div id="box"></div>
<script>
/*
el 元素
attr 样式
val 目标点
cb 动画结束之后,要做的事情
如果目标值 比 当前值大 ,返回一个正数,否则返回一个 负数
*/
function move(el,attr,val){
let now = parseFloat(getComputedStyle(el)[attr]);
let speed = val>now?1:-1;
return new Promise((resolve)=>{
clearInterval(el.timer);
el.timer = setInterval(() => {
if(Math.abs(now - val) <= 0 ){
clearInterval(el.timer);
resolve();
} else {
now += speed;
el.style[attr] = now + "px";
}
}, 16);
});
}
{
let box = document.querySelector("#box");
function boxMove(){
move(box,"left",200).then(()=>{
return move(box,"top",200);
}).then(()=>{
return move(box,"left",0);
}).then(()=>{
return move(box,"top",0);
}).then(()=>{
boxMove()
});
}
boxMove();
}
</script>
</body>
</html>
实际工作中promise用域接口请求的实例
new Promise((resolve)=>{
// 请求接口拿到数据
setTimeout(()=>{
let data = {
state: 200,
isState:"ok",
data: {
code: 1
}
}
resolve(data);
},100);
}).then((data)=>{
// if(data.state == 200){
// }
return data.data;
}).then((data)=>{
console.log(data);
});
七、Promise 的其他用法
1.Promise.reject
Promise.reject(reason) 返回一个状态为 Rejected 的 Promise 对象
参数:
reason 失败原因
2.Promise.resolve
Promise.resolve(value) 返回一个状态为 resolved 的 Promise 对象
参数:value 将被Promise对象解析的参数
3.Promise.catch
捕获前一个promise抛出的错误
new Promise((resolve,reject)=>{
reject(1);
}).then(()=>{
console.log(1);
return Promise.reject() // 不需要处理异步的业务,直接返回一个 reject 状态的 Promise 对象
}).then(()=>{
console.log(2);
}).catch((err)=>{ // 捕获 前边的 Promise 中出的错误
console.log("出错了",err);
});
4.Promise.all
Promise.all方法用于将多个Promise实例,包装成一个新的Promise实例,当所有Promise都成功的时候,整个Promise.all才成功
5.Promise.race
与Promise.race方法类似将多个promise包装成一个新的promise实例
但是其中有一项的状态发生改变新的实例的状态就会随着改变
let p = new Promise((resolve)=>{
setTimeout(()=>{
console.log(1);
resolve();
},10);
});
let p2 = new Promise((resolve)=>{
setTimeout(()=>{
console.log(2);
resolve();
},1000);
});
let p3 = new Promise((resolve)=>{
setTimeout(()=>{
console.log(3);
resolve();
},3000);
});
// Promise.all([p,p2,p3]).then(()=>{
// console.log(4);
// });
Promise.race([p,p2,p3]).then(()=>{
console.log(4);
});
八、async函数
只要函数名之前加上async关键字,就表明该函数内部有异步操作。
该异步操作应该返回一个Promise对象,前面用await关键字注明。
当函数执行的时候,一旦遇到await就会先返回,等到触发的异步操作完成,再接着执行函数体内后面的语句
async函数其实是在Promise 的基础上对异步方法的改写的一个新的方案
没有使用async 的Promise,如果业务需求复杂的时候,代码看起来非常冗余
new Promise((resolve)=>{
setTimeout(()=>{
console.log(1);
resolve();
},1000);
}).then(()=>{
return new Promise((resolve)=>{
setTimeout(()=>{
console.log(2);
resolve();
},1000);
});
}).then(()=>{
return new Promise((resolve)=>{
setTimeout(()=>{
console.log(3);
resolve();
},1000);
});
}).then(()=>{
console.log("执行完成")
});
使用async 之后,看起来会稍微好点
async function fn(){
try{
let n1 = await new Promise((resolve)=>{
setTimeout(()=>{
console.log(0);
resolve(1);
},1000);
});
let n2 = await new Promise((resolve)=>{
setTimeout(()=>{
console.log(n1);
resolve(2);
},1000);
});
let n3 = await new Promise((resolve)=>{
setTimeout(()=>{
console.log(n2);
resolve(3);
},1000);
});
console.log(n3);
}catch(err){
console.log(err);
}
}
fn();
扩展
回调地狱
- 封装简易运动框架
- 最早我们处理异步消息通知,都是通过回调来处理的,但是回调多了,代码的结构就必然嵌套层级特别多,造成可读性和维护性的直线下降 - 这就是回调地狱