目录
2.如果给同一个promise指定多个成功/失败的回调函数,都调用吗?
4.promise.then()返回的新的promise的结果状态由什么决定?
基本概念
实例对象:通过new 函数产生的对象
函数对象:将函数作为对象使用时,叫函数对象。
function Fn(){ //构造函数
}
const fn = new Fn() // fn——实例对象
console.log(Fn.protoype) // 通过.来访问函数Fn的属性,此时Fn是函数对象
Fn.call({}) // Fn是函数对象,所有函数对象都有这个call()属性,所以call其实定义在Function.prototype上
回调函数:
1.同步回调: 立即执行,完全执行完成才结束,不放入回调队列中
eg:数组遍历的回调函数map/filter/reduce等 / Promise的excutor函数
2.异步回调:不会立即执行,而是放进回调队列中
eg:setTimeout(()=>{},s) 里面的回调函数
Pomise是什么?
1.理解: Promise是Js中进行异步编程的的新的解决方案(旧的是指纯回调函数形式,容易产生回调地狱)
2.具体:
-
从语法上来说:Promise就是一个构造函数
-
从功能上来说:promise对象用来封装一个异步操作,还能获取这个操作的结果。
3.promise的状态改变:一个promise对象只能改变一次
-
pending ——> resolved
-
pending ——> rejected
Promise基本使用:
// 1.创建一个新的promise对象
const p = new Promise((resolve,reject)=>{ //执行器函数
// 2.执行异步任务
setTimeout(()=>{
const time = Date.now();
// 3.1 如果成功,调用resolve(value)
if (time %2 == 0){
resolve('成功'+time)
}else { // 3.2 如果失败,调用reject(reason)
reject('失败'+time)
}
},1000)
})
p.then(
value => { //接收得到的成功的value数据 onResolved
console.log('成功的回调',value)
},
reason =>{ // 接收得到的失败的reason数据 onRejected
console.log('失败的回调',reason)
}
)
Promise和纯回调函数对比:
纯回调函数: 需要先指定好回调函数,再调用回调函数来执行异步任务
Promise:可以先启动异步任务,(即在执行器函数中启动异步任务(即加入任务队列之中,因为执行器函数是同步回调,在new Promise的时候就执行) ),甚至可以在 异步任务执行完成之后再指定回调函数。
所以Promise的优势:
-
指定回调函数的方式更加灵活:
-
旧的:必须在启动异步任务之前指定好
-
promise:启动异步任务—>返回promise对象—>给promise对象绑定回调函数(甚至可以在异步任务结束后)
-
-
支持链式调用,可以解决回调地狱问题
-
什么是回调地狱? 回调函数嵌套使用,外部回调函数异步执行的结果是嵌套的回调函数执行的条件
-
回调地狱的缺点? 不便于阅读,不便于异常处理
-
解决方案? promise链式调用 ——>async/await
-
Promise的API:
1.Promise构造函数:Promise(excutor) {}
excutor函数:同步执行: (resolve,reject )=>{}
resolve函数:excutor函数中成功时调用的函数 : value=>{ }
reject函数:excutor函数中失败时调用的函数:reason=>{ }
excutor 在promise中立即同步执行,异步操作封装在excutor函数中执行
2.Pomise.prototype.then()方法:(onResolved,onRejected)=>{ }
onResolved函数:成功的回调函数
onRejected函数:失败的回调函数
用于指定成功或者失败的回调函数,返回一个新的promise对象
3.Promise.prototype.catch() 方法:(onRejected) =>{ }
用于指定失败的回调函数,相当于 then(null,onRejected)
4.Promise.resolve()方法 :(value) => { }
value:成功的数据或者是一个promise对象
返回的是一个成功或者失败的promise对象
5.Promise.reject()方法
// 产生一个成功值为1的promise对象
const p1 = new Promise((resolve,reject)=>{
resolve(1)
})
// 语法糖
const p2 = Promise.resolve(1)
6.Promise.all() 方法:(promises) =>{ }
promises:包含n个promise的数组
返回一个新的promise,只有所有的promise都成功才算成功,有一个失败就失败了
7.Promise.race()方法: (promises)=>{ }
返回一个新的promise,是第一个完成的promise的结果状态。
Promise的注意点:
1.改变promise状态的三种方式
-
resolve(value): pending——>resolved
-
reject(reason): pending——>rejected
-
throw 异常:pending——>rejected (reason就是throw出来的Error)
2.如果给同一个promise指定多个成功/失败的回调函数,都调用吗?
当promise改变状态时都会调用
3.改变promise状态和指定回调函数的顺序?
-
顺序不一定,正常来说是应该先指定回调函数再改状态,但是也可以先改状态再指定回调
-
如何先改状态再指定回调?
-
在执行器函数里面直接执行resolve()/reject()
-
延迟更久的事件才调用then()
-
-
什么时候得到数据?
-
如果先指定回调函数,那么当状态改变时,就直接调用回调函数,得到数据
-
如果先改变状态,那么当你指定回调函数时,才调用回调函数,得到数据
-
// 1.先指定回调函数,后改状态
new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(1); // 异步执行回调函数 ——后改状态,同时指定数据
},1000);
}).then( // 同步指定回调函数,保存当前指定的回调函数 ——先指定回调函数
value =>{console.log('value1',value)},
reason=>{}
)
// 2.先改状态,后指定回调函数(1)
new Promise((resolve,reject)=>{
resolve(1); //先改状态,同时指定数据
}).then( // 后指定回调函数,异步执行回调函数
value =>{console.log('value2',value)},
reason=>{}
)
// 3.先改状态,后指定回调函数(2)
const pro = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(1);
},1000);
});
setTimeout(()=>{
pro.then(
value=>{console.log('value3',value)},
reason=>{}
)
},3000)
4.promise.then()返回的新的promise的结果状态由什么决定?
-
由then()指定的回调函数的执行结果决定
-
如果抛出异常,新的promise状态时rejected,reason就是抛出的err
-
如果返回的是一个非promise的值,新的promise就变成resolved,value为这个返回值
-
如果返回的是另一个新的promise,那么这个新的promise的结果就是then()返回的promise的结果
-
5.promise如何串连多个操作任务?
-
因为promise的then()返回的是新的promise,可以利用then()来链式调用
-
通过then的链式调用串联多个同步/异步任务
// 链式调用
new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(1);
})
}).then(
value=>{
console.log('任务1的结果:',value);
console.log('执行任务2(同步)');
return 2;
}
).then(
value=>{
console.log('任务2的结果',value);
//启动异步任务
return new Promise((resolve,reject)=>{
setTimeout(()=>{
console.log('执行任务3(异步)');
resolve(3)
},1000)
}).then(
value=>{
console.log('任务3的结果',value)
}
)
}
)
6.promise异常传透?
-
当promise进行链式调用时,可以在最后指定失败的回调
-
当前任何操作出了异常,都会传到最后失败的回调中进行处理
-
没有写onRejected函数,相当于 reason=>{throw reason},逐级向下抛,最后找到尾巴处的catch
7.如何中断promise链?
-
可以在回调函数中间返回一个pending状态的promise对象
-
这样就可以在链式调用的中间中断,不再调用后面的回调函数
async 和 await
1.async函数
-
函数的返回值是promise对象
-
promise对象的结果由async函数执行的返回值决定
// async 函数返回值是一个promise对象
// async 函数返回的promise的结果由函数执行的结果决定
async function fn1(){
// return 1; // 返回的是成功的promise
// throw 2; // 返回的是一个失败的promise
// return Promise.resolve(2);
return new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(4);
},1000)
})
}
const result = fn1();
result.then(
value => {console.log('onResloved()',value)},
reason => {console.log('onRejected()',reason)}
)
2.await 表达式
-
await右侧的表达式一般是promise对象,也可以是其他值
-
如果表达式是promise对象,那么 await返回的是promise 成功的值
-
如果表达式是其他值,那么这个值就是await的返回值
function fn2(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
// resolve(5);
reject(7);
},1000)
})
}
async function fn3(){
// const value = await fn2(); // await 右侧表达式是promise,得到的结果就是promise成功的value
// const value = await 6; // await 右侧表达式是一个其他值,得到的结果就是这个值本身
try {
const value = await fn2();
console.log('value',value);
}catch (error){
console.log('失败的结果',error);
}
}
fn3();
3.注意:
-
await必须写在async函数里面,async函数里面可以没有awiat
-
如果await的promise失败了,会抛出异常,所以await要写在try…catch里面
宏队列和微队列
执行回调函数的队列分为宏队列和微队列
-
宏队列:用来保存待执行的宏任务(回调),定时器回调/DOM事件回调/ajax回调
-
微队列:用来保存待执行的微任务(回调),promise回调/mutationObserver回调
-
执行顺序:js引擎 先执行所有初始化同步任务, 每次从宏队列取出第一个宏任务执行之前,都要将所有的微任务一个一个取出来执行