Promise学习笔记
一、概念
Promise对象是JavaScript异步操作解决方案,为异步操作提供统一接口。Promise对象的三种状态:未完成(pending)、已完成(fulfilled)、失败(rejected)
二、对象的使用
1、封装函数创建对象
function getFoo() {
let p=new Promise((resolve,reject)=>{
if (/* 异步操作成功 */true) {
resolve(value);
}else{
reject(error);
}
});
return p;
}
2、then( )方法
指定resolve方法和reject方法的回调函数
const prom=getFoo();
prom.then(v=>{
// success
},e=>{
// failure
})
三、JavaScript异步
1、同步
function fun1() {
console.log("函数1");
}
function fun2() {
console.log("函数2");
}
fun1();
fun2();
2、异步
a、同步回调
function fun1(fun) {
console.log("函数1");
fun();
}
function fun2() {
console.log("函数2");
}
fun1(fun2);
b、异步回调
function fun1(fun) {
setTimeout(function () {
fun();
}, 0);
console.log("函数1");
}
function fun2() {
console.log("函数2");
}
fun1(fun2);
for (let i = 0; i <= 10000; i++) {
console.log("i:", i);
}
在异步回调时,就算fun2()函数放在最上面,而且fun1()函数外面有一个大的for循环,打印结果:
函数1
i:0
......
i:10000
函数2
而且,setTimeout函数的延时为0;到此,我对异步的理解为,当JS文件内所有的同步事件结束后,再回过头来执行所有的异步事件,而异步事件也是按照其所在的异步事件等级(比如异步中包含异步,形成第一级异步、第二级异步…)形成同等级异步的同步事件。(后来我又看到,异步事件又分为宏队列和微队列,详见四)
c、ajax异步
AJAX中根据async的值不同分为同步(async = false)和异步(async = true)
在异步模式下:
****首先:js向服务器发出请求,在等待数据时,ajax后面的同步事件按序依次执行。这样,如果等待数据的时间过长或者等不到数据,都不影响其他事件的执行。
****其次:当数据返回成功时,ajax调用回调函数处理数据。这里有个问题,就是数据返回得很快,js也不是立即执行回调函数,由于是异步事件,需要等同步事件都执行完后再开始执行异步事件。也就是,如果js文件的同步事件执行完需要10分钟(夸张了点),而ajax数据1分钟就返回成功了,但是对不起,ajax的回调函数得10分钟后执行。
$.ajax({
type: "get",
url: "./data/productlist.json",
dataType: "json",
success: function (data) {
console.log(data);
},
});
function fun1(fun) {
setTimeout(function () {
fun();
}, 0);
console.log(1);
}
function fun2() {
console.log(2);
}
fun1(fun2);
for (let i = 0; i <= 500; i++) {
console.log("i:", i);
}
for (let i = 0; i <= 500; i++) {
console.log("b:", i);
}
再次:在上例中,ajax异步和fun2异步两个回调函数,处于同级别异步事件中,按次序执行。这样就又回到了原问题,当ajax异步回调函数执行时间过长时,将会影响到其他异步事件的执行。有办法,让异步函数的的回调函数,也变成异步,由此,**“回调地狱”**到来了。
$.ajax({
type: "get",
url: "./data/productlist.json",
dataType: "json",
success: function (data) {
setTimeout(function () {
console.log(data);
}, 0);
},
});
3、ES6新的对象Promise
Promise提供了一种更合理、更强大的异步解决方案,回调函数变成了链式写法,程序的流程可以看得很清楚,而且有一整套的配套方法,可以实现许多强大的功能。
const prom=getFoo();
prom.then(v=>{
// success
},e=>{
// failure
})
prom.then(回调函数),而且,prom.then(回调函数)也是一个Promise对象,**“回调地狱”**可以写成prom.then(回调函数1).then(回调函数2).then(回调函数3).then(回调函数4)…
网上一个同学的面试题:https://www.jianshu.com/p/c613c0198430
var promise = new Promise(function(resolve, reject) {
console.log('Promise instance');
resolve();
});
promise.then(function() {
console.log('resolved result');
});
for(var i=0;i<100;i++) {
console.log(i);
}
程序的打印顺序
4、迭代器(Iterator)和生成器(Generator)
ES6新增的异步操作的方法,还没有好好学
5、async和await
在ES7中,async函数处理异步,是生成器的一种语法糖,简化了外部执行器的代码,同时利用await替代yield,async替代生成器的(*)号。
参考:https://zhuanlan.zhihu.com/p/74637286
四、回调队列和事件循环(event loop)
1、event loop
event loop它最主要是分三部分:主线程、宏队列(macrotask)、微队列(microtask)。
js的任务队列分为同步任务和异步任务,所有的同步任务都是在主线程里执行的,异步任务可能会在macrotask或者microtask里面。
2、宏队列
setTimeout、setInterval、setImmediate、I/O、UI rendering
DOM 的回调、Ajax 的回调
3、微队列
promise.then、process.nextTick
4、执行顺序(同步->微任务->宏任务)
• JS 引擎先去执行所有的同步代码
• 然后执行队列中的任务
• 在一个一个宏任务执行前,会先检查微任务的执行,必须等所有微任务执行完才会一个一个去执行宏任务
5、流行的面试题
async function asycn1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2');
}
console.log('script start');
setTimeout(() => {
console.log('setTimeout ');
}, 0);
asycn1();
new Promise((resolve) => {
console.log('promise1');
resolve();
}).then(() => {
console.log('promise2');
});
console.log('script end');
参考:https://blog.csdn.net/weixin_41884153/article/details/90737448
https://www.cnblogs.com/BAHG/p/12921321.html
https://blog.csdn.net/weixin_44138611/article/details/112464382
五、then方法中返回return
应用场景:在页面加载时,异步函数A获得对象数组arr,再调用一个函数B对数组arr进行过滤,而这个函数B里有用另一个异步函数C的结果再进行一次过滤,过滤出来的结果返回给A继续使用处理。
一个问题:如果函数B是个普通函数,那么异步函数C里的结果resultArr我没有办法return resultArr出来,需要将函数B做成Promiss对象
// 函数A
getObjectData('objDetail').then(res => {
payMonth(res,selectMonth.split('-')[1],selectMonth.split('-')[0]).then(res=>{
console.log(res);
})
})
// 函数B
// 月支出总计
export function payMonth(arr,month,year){
let dateArr=new Promise((resolve,reject)=>{
let monthArr=[]
let payClassMon=''
let payArr = arr.filter(item =>
item.incomePay == 0 && item.date.split(' ')[0].split('-')[0] == year && item.date.split(' ')[0].split('-')[1]==month
)
if (payArr) {
// 月总支出
let pay = payArr.reduce((total, value, index, array) => {
return total + parseFloat(value.money);
}, 0)
monthArr.push({'payMonthCount':pay})
// 类别 函数C
getObjectData('classIncome').then(res=>{
if (res) {
for(let ele of res){
let payClassArr = payArr.filter(item =>
item.aclass==ele
)
if (payClassArr.length>0) {
payClassMon = payClassArr.reduce((total, value, index, array) => {
return total + parseFloat(value.money);
}, 0)
monthArr.push({'aclass':ele,'classPay':payClassMon})
}
}
}
resolve(monthArr);
})
}
})
return dateArr;
}