一、什么是Promise?
Promise 是异步编程的一种解决方案:从语法上讲,promise是一个对象(Promise对象是一个构造函数,用来生成Promise实例),从它可以获取异步操作的消息;从本意上讲,它是承诺,承诺它过一段时间会给你一个结果。
Promise 不解决异步问题,解决的是异步的写法
二、语法
new Promise( function(resolve, reject) {...} /* executor */ );
let p = new Promise(function(resolve,reject){// 这个函数会被 PromisesetTimeout(()=>{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 = `/timg?image&quality=80&size=b9999_10000&sec=1576855691894&di=b694b104df1eb0b53ee6b52d8ad6fb49&imgtype=0&src=http%3A%2F%%2F-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();
扩展
回调地狱
封装简易运动框架最早我们处理异步消息通知,都是通过回调来处理的,但是回调多了,代码的结构就必然嵌套层级特别多,造成可读性和维护性的直线下降 - 这就是回调地狱