Promise
Promise是什么?Promise:承诺者模式,它是ES6新增的一个内置类,基于Promise可以有效管理“异步编程”,避免回调地狱从语法上来说promise是一个构造函数从功能上来说promise对象来封装一个异步操作,并可获取其成功/失败的结果值什么是回调地狱?JQ中的ajax管理,是基于回调函数的方式管理的「请求成功后,会触发success回调函数执行,result就是本次请求获取的结果」
目录
思维导图
Promise是什么?
Promise:承诺者模式(承诺异步编程结束后,根据处理的状态和结果,通知fulfilled/reject)执行 它是ES6新增的一个内置类,基于Promise可以有效管理“异步编程”,避免回调地狱
- 从语法上来说promise是一个构造函数
- 从功能上来说promise对象来封装一个异步操作,并可获取其成功/失败的结果值
兼容问题
new promise(...) 不兼容ie浏览器(EDGE可以兼容) 真实项目开发的时候,如果使用了promise还要兼用ie浏览器需要处理兼容
- @1 babel/preset语法包,无法编译promise 他只能编译 let/const这种常规语法
- @2 我们需要基于@babel/polyfill实现promise的兼容处理(原理基于es5手动实现的一个promise)
什么是回调地狱?
JQ中的ajax管理,是基于回调函数的方式管理的「请求成功后,会触发success回调函数执行,result就是本次请求获取的结果」;
串行:如果我们想实现ajax串行,需要把下一个请求发送,放在上一个请求成功的回调函数中处理,如果有多个串行的请求,就会一层层的嵌套... => “回调地狱”「代码看起乱、不方便管理」
并行:请求之间没有啥依赖,可以同时发送多个请求(一般会额外处理一些事情:等待所有请求都成功,统一干啥事)
痛点:传统方案中,基于回调函数的方式管理异步编程的代码,总是要在异步任务可执行的时候,在他的回调函数中处理一些事情,这样很容易就产生回调地狱!!
// 存在三个:a/b/c.txt文件,想通过a文件获取c文件中的内容,
// ,如果用普通的ajax,就会产生ajax中嵌套
$(function(){
$.get("a.txt",function(data){//第一层嵌套
$.get(`${data}.txt`,function(data){//第二层嵌套
$.get(`${data}.txt`,function(data){//第三层嵌套
console.log(data);
})
})
})
promise解决回调地狱
const http1 = () => {
return new Promise(resolve => {
$.ajax({
url: '/api/test1',
success: resolve
});
});
};
const http2 = () => {
return new Promise(resolve => {
$.ajax({
url: '/api/test2',
success: resolve
});
});
};
const http3 = () => {
return new Promise(resolve => {
$.ajax({
url: '/api/test3',
success: resolve
});
});
};
http1()
.then(value => {
console.log('第一个请求结果', value);
return http2()
})
.then(value => {
console.log('第二个请求结果', value);
return http2()
})
.then(value => {
console.log('第三个请求结果', value);
return http3()
});
(async function () {
let value = await http1()
console.log('第一个请求结果', value);
value = await http2()
console.log('第二个请求结果', value);
value = await http3()
console.log('第三个请求结果', value);
})()
为什么要用promise?
1.指定回调函数的方式更加灵活
- 旧的:必须在启动异步任务前指定
- promise 启动异步任务==>返回promis对象==>给promise对象绑定回调函数(甚至可以在异步任务结束后指定/多个)
2.支持链式调用,可以解决回调地狱问题
详细讲Promise
一.实例对象私有属性方法
promise的状态
实例对象中的一个属性:[[PromiseState]]
- - ==pending==:等待状态 (默认)
- - ==fulfilled/resolved==:成功状态
- - ==rejected==:失败状态
pending变为resolved
pending变为reject
说明:只有两种且一个promise只能改变一次
无论成功失败都有一个结果数据
成功的数据一般称为value失败的结果数据一般称为reason
PromiseResult promise对象的值
- 实例对象中的另一个属性[PromiseResult]
- 保存着异步任务[成功/失败]的结果
- 状态为padding时,默认是undefined
promise执行过程
如何使用promise
二.promise构造函数new Promise([executor]);
- executor:必须是一个函数,传递的不是函数会报错
- executor函数执行的时候,会接收两个实参值,我们分别用resolve/reject形参来存储
- executor函数:执行器(resolve,reject)=>{}
- resolve函数:内部定义成功时我们调用函数value=>{},把创建的实例状态改为成功态(fulfilled)实例的值为value成功的结果
- reject函数:内部定义失败时我们调用函数reason=>{}把创建的实例状态改为失败态(rejected)实例的值为reason失败的原因
- 如果executor函数执行过程中,有异常错误抛出,则实例的状态仍然是rejected实例的值是报错的原因
- 一但状态改为fulfilled或者rejected则不能在次修改
- 说明executor会在promise内部立即同步调用,异步操作在执行器中进行
let p = new Promise((resolve, reject) => {
// 同步调用
console.log(111);
})
console.log(222);
三. 原型对象属性方法(实例可以调用的公有属性方法)
promise.prototype.then方法(onResolved,onRejected)=>{}
- promise实例.then([onResolved],[onRejected])
- 实例状态为成功则把onResolved函数执行,反之实例状态是失败onRejected函数执行并且把实例状态的值(成功的结果失败的原因)作为实参传递给某个函数
- 改变promise实例的状态和值,就是为了控制then中的两个方法谁来执行,以及实参值value/reson是多少
- onResolved函数:成功的回调函数(value)=>{}
- onRejected函数:失败的回调函数(reason)=>{}
- 说明:指定用于value的成功回调和用于的到失败reason的失败回调返回一新的promise对象,主要目的是为了实现链式写法
- 新的promise对象它的状态和值有上一个实例.then传递的onfulfilled或者onrejected执行决定
let p = new Promise((resolve, reject) => {
// resolve(console.log(a)); 语法错误失败
resolve("成功")
reject("失败")
})
p.then((value) => { console.log(value); },
(reason) => { console.log(reason); })
promise.prototye.catch方法:(onRejected)=>{}
onRejected函数:失败的回调函数(reason)=>{},
如果实例状态是失败,而我们也没有去设置onreject进行处理,那么控制台会抛出异常,但是不影响其他代码执行,所以我们要在then链的最末尾设置一个catch,这样不论哪一级返回失败的promise实例,都会把catch的onreject执行
let p = new Promise((resolve, reject) => {
reject("失败")
})
p.catch((reason) => {
console.log(reason);
})
promise.prototye.finally方法:
不论成功失败都会走finally方法
promise.prototye.symbol(Symbol.toStringTag:Promise)方法
四.promis作为普通对象,具备静态私有属性方法
Promise.resolve:方法(value)=>{}
- value成功的数据或promise对象
- 如果传入的参数为 非promise类型的对象,则返回的结果为非promise对象
- 如果传入的参数为promise对象,则参数的结果决定了resolve的结果
- 说明:返回一个成功或失败的promise对象
let p1 = Promise.resolve(521)
//如果传入的参数为 非promise类型的对象,则返回的结果为非promise对象
//如果传入的参数为promise对象,则参数的结果决定了resolve的结果
let p2 = Promise.resolve(new Promise((resolve, reject) => {
// resolve("OK")
reject("失败")
}))
console.log(p2);
Promise.reject方法(reason)=>{}
- reason失败的原因
- 说明:返回一个失败的promise对象
let p = Promise.reject(521)
let p2 = Promise.reject("iloveyou")
let p3 = Promise.reject(new Promise((resolve, reject) => {
resolve("ok")
}))
console.log(p3);
//失败的原因是成功的promise对象
promise.all方法[promises]=>{}
- promises包含了n个promise的数组,如果其中一项不是promise实例,则浏览器也会默认把其变为状态是fulfilled,(成功)值是自身的实例
- 说明:返回一个新的promise,只有所有的promise都成功才成功,只要有一个失败的了就直接失败
let p1 = new Promise((resolve, reject) => {
resolve("ok")
})
let p2 = Promise.resolve("成功")
let p3 = Promise.resolve("oh year")
const result = Promise.all([p1, p2, p3])
console.log(result);
promise.race方法[promises]=>{}
- promises包含了n个promise的数组
- 说明:返回一个新的promise,第一个完成的promise的结果是最终的结果状态
promise.any方法
集合中只要有一项是成功的,则返回一个新的promise值是成功项的值;所有项都是失败才是失败
promise.allSettled方法
解决回调地狱
- 注意事项:
- - 执行函数中执行resolve方法会修改为成功状态
- - 执行函数中执行rejecte方法或者有执行代码==报错==,会修改为失败状态
- - then方法无论执行哪种状态的回调函数,都会默认return一个新的Promise实例resolve状态的对象,默认值为undefined,通过return res的方式可以给下一次的函数中传值。
- - 如果想让then方法的回调函数返回状态为rejected的promise实例,就需要return new rejected对象,或者throw一个错误。
function getdata(param){
return new Promise(function(resolve,reject){
resolve(param);//修改状态为resolved/fulfilled
reject(200);//由于承诺者模式,一但状态修改过后,不会再次修改,所以不会执行它
})
}
getdata(a).then(
function(res){ return res },//处理p成功后的结果,res为传过来的参数100
function(rej){ return rej})//处理p失败状态后的结果,rej为传过来的参数200
.then(
function(res){res},
function(rej){rej})
promise的关键问题
1.如何改变promise的状态?
- resolve(value)如果当前是pending就会改变为resolved
- reject(reason)如果当前是pending就会改变为rejected
- 抛出错误如果当前是pending就会改变为rejected
2.一个paroise指定多个成功/失败回调函数,都会调用吗?
当promise改变为对应状态时都会调用
3.改变promise状态和指定回调谁先谁后?
都有可能,正常情况下指定回调在改变状态,但也可以先改状态在指定回调
如何先改状态在指定回调?,
- 在执行器中直接调用resolve()/reject();
- 延迟更长时间在调用then'
什么时候才能得到数据?
- 如果先指定回调那当状态发生改变时,回调函数会调用得到数据
- 如果先改变状态,那当指定回调时,回调函数会调用,得到数据
4.promise.then()返回的promise的结果状态由什么决定?
- 如果抛出异常, 新promise变为rejected,reason为抛出异常
- 如果返回的是非promise的任意值,新promise变为resolved (成功),值是return返回的值没有写return 值为undefined
- 如果返回的是另一个新的promise此promise的结果为新promise的结果
5.promise如何串联多个操作任务?,
- promise的then()返回一个新的promise,可以开成then()的链式调用
- 通过then的链式串联多个同步/异步任务
6.then链的穿透和顺延机制
- 执行then如果ononfulfilled/onrejected没有传递则顺延到下一个同等状态的方法上
- 当使用promise的then链式调用时,可以在最后指定失败原因
- 前面的任何操作出了异常都会传到最后失败的回调中处理
7.如何中断promise链?
- 当使用promise的then链式调用时,在中间中断,不在调用后面的函数
- 在回调函数中返回一个pendding状态的promise对象
async与await
只能出现在函数中promise+generator的语法糖
async函数
- 函数的返回值为promise对象
- 如果函数中使用await,则函数必须经过async修饰
- promise对象的结果由async函数的执行返回值决定
async function main() {
// 返回值是一个非promise对象
return 521
// 返回一个promise对象
return new Promise((resolve, reject) => {
// reject("Error")
resolve("ok ")
})
//抛出异常
throw "OH no"
}
let result = main();
console.log(result);
await表达式
- 可以把异步操作改为类似于同步的效果,
- await右侧表达式一般为promise对象,但也可以是其他类型的值,
- 如果表达式是promise对象 await返回的是promise成功的值
- 如果表达式是其他的值,直接将此值作为await返回的值
- 需要等待后面的promise实例状态为成功才会执行当前上下文await下面的代码,如果后面实例是失败的则下面代码不执行
注意
- await必须写在async函数中但async函数可以没有await
- 如果await的promise失败了 就会抛出异常通过 try catch捕获处理,在catch中处理状态是失败要做的事情
-
等待,我们一般在其后面放promise实例,她会等待实例状态为成功,再去执行“当前上下文”中,await下面的代码「如果promise实例管控的是一个异步编程,其实它是在等待异步成功,再执行下面代码,类似于把异步改为同步效果」
async function main() {
let p = new Promise((resolve, reject) => {
// resolve("ok");
reject('Error')
})
// 右侧为promise的情况
// let res = await p
//如果右侧为其他类型
// let res2 = await 20
//如果promise是失败状态
try {
let res = await p;
} catch (e) {
console.log(e);
}
}
main()
关于async/await中的同步异步
await后面需要跟一个promise实例=>await100 也会变成promise.reslove(100)
当前上下文中await下面的代码,需要等待promise状态实例确定
- 成功下面的代码可以执行
- 失败/pending下面的代码不会执行
await下面代码是一个异步微任务
- 当前上下文中,只遇到await把下面的代码设置为一个异步微任务,放在webAPI(消息队列)中监听
- 接下来等到await后面的promise实例的状态,如果状态变为成功了,再把下面的异步微任务放在等待对列中排队等待,等主线程空闲下来之后,在拿出来执行
.Promise中的then方法是如何实现同步异步的?
new Promise([exector]):exector执行函数是同步的
- 在执行到p.then()方法时,存在两种情况:
- - 情况一:**已知p的结果状态以及值**:但是没有立即执行[onfulfilled]/[onrejected],此时会创建一个可执行的异步微任务(==只存放p状态对应的方法==),放在WebAPI中监听,但是因为已经知道是可执行的,所以立即挪至EventQueue中排队等待...
- - 情况二:**还不知道p的结果状态以及值**:更无法知道该执行[onfulfilled]/[onrejected],此时先把p.then()方法存放在WebAPI中,直到p实例的状态确定后,再通知p.then()方法执行【通知也是一个异步微任务】
- 后期我们基于reslove/reject把实例状态立即修改后,也并不会立即通知,事先存储[onfulfilled]/[onrejected]的方法执行,而是创建一个异步微任务
延迟函数/休眠函数如何实现
延迟函数、休眠函数:表示当前上下文中,当执行到某函数时,会暂停代码执行,可以自己定义停止多长时间
- 使用async/await和promise来实现:
-----------------------------------------------完结----------------------------------------------------------------------
-----------------------接受大佬们的批改,欢迎留言评论-----------------------------------------------------------
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)