基础概念
Web页面是单线程架构模型,决定了编写代码的形式为异步编程。Promise是ES6引入的异步编程的新解决方案(传统解决方案:回调函数+事件),通过回调任务的层层嵌套来保证异步任务的执行顺序,但是会出现回调地狱。
Promise是一个包含异步操作结果的对象。Promise解决的是异步编码风格的问题。
Promise对象有两个特点:
- 对象的状态不受外界影响。有三种状态:pending、resolved、rejected。只有异步操作的结果可以决定是哪一种状态,任何其他操作无法改变这种状态。
- 一旦状态改变,就不会再变,且任何时候都可以获得这个结果。pending→resolved,pending→rejected。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject,它们也是两个函数,由 JavaScript 引擎提供,不用自己部署。let p=new Promise((resolve,reject)=>{})
resolve:pending→resolved,将异步操作的结果作为参数传递出去;
reject:pending→rejected,将异步操作的错误作为参数传递出去。
一个例子:
const imgurl='11.webp';
const imgPromise=function(url){
return new Promise((resolve,reject)=>{
const img=new Image();
img.src=url;
img.onload=()=>resolve(
img
);
img.onerror=()=>reject(
new Error('出错了')
)
})
}
imgPromise(imgurl).then(img=>
{document.body.appendChild(img)}
).catch((error)=>{document.body.innerHTML=error})
方法:
then方法的作用是:为Promise实例添加状态改变时的回调函数,接收两个回调作为参数:状态变为resolved时的回调和状态变为rejected时的回调
then的参数:回调函数的返回值:
1,返回Promise实例对象,返回的该实例对象会调用下一个then
2,返回普通值,直接传递给下一个then,通过then参数中函数的参数接受该值。这种情况下then会默认产生一个Promise实例对象。
catch:一般来说,不要在then()方法里面定义 Reject 状态的回调函数,总是使用catch方法:
p.then(function(value){
//success
})
.catch(function(error){
//failure
}).finally(function(){
});
finally:指定不管最后状态如何都会执行的回调函数
Promise.all()并发处理多个异步任务,所有任务都执行完成才能得到结果;
Promise.race()并发处理多个异步任务,只要有一个任务完后就能得到结果。
then和catch都属于实例方法,通过实例调用(定义在Promise原型链上),all和race是两个对象方法,直接通过Promise对象调用。
一些问题
为什么需要Promise有了Promise对象,可以将异步操作以同步操作的流程表达出来,避免了回调地狱。
Promise——消灭嵌套调用;错误合并处理。
Promise 是怎么消灭嵌套回调的?
产生嵌套函数的一个主要原因是在发起任务请求时会带上回调函数,这样当任务处理结束之后,下个任务就只能在回调函数中来处理了。Promise 主要通过下面两步解决循环嵌套回调问题的。首先,Promise 实现了回调函数的延时绑定。
let x1=new Promise(executor);
let x2=x1.then(onResolve);//onResolve这个回调函数延迟绑定,执行到then时才会调用onResolve
其次,需要将回调函数 onResolve 的返回值穿透到最外层。
Promise 中是如何实现回调函数返回值穿透的?
首先Promise的执行结果保存在promise的data变量中,然后是.then方法返回值为使用resolved或rejected回调方法新建的一个promise对象,即例如成功则返回new Promise(resolved),将前一个promise的data值赋给新建的promise
Promise 是怎么错误合并处理的?
之所以可以使用最后一个对象来捕获所有异常,是因为 Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被 onReject 函数处理或 catch 语句捕获为止。具备了这样“冒泡”的特性后,就不需要在每个 Promise 对象中单独捕获异常了。
Promise 出错后,是怎么通过“冒泡”传递给最后那个捕获异常的函数?
promise内部有resolved_和rejected_变量保存成功和失败的回调,进入.then(resolved,rejected)时会判断rejected参数是否为函数,若是函数,错误时使用rejected处理错误;若不是,则错误时直接throw错误,一直传递到最后的捕获,若最后没有被捕获,则会报错。可通过监听unhandledrejection事件捕获未处理的promise错误。
Promise 中为什么要引入微任务?
由于promise采用.then延时绑定回调机制,而new Promise时又需要直接执行promise中的方法,即发生了先执行方法后添加回调的过程,此时需等待then方法绑定两个回调后才能继续执行方法回调,便可将回调添加到当前js调用栈中执行结束后的任务队列中,由于宏任务较多容易堵塞,则采用了微任务,保证了实时性和效率。