异步处理
回调函数
概念:一个函数作为另一个函数的参数;先执行一个函数,把他的执行结果作为另一个函数的参数;
弊端:会产生回调地狱。(回调函数的层层嵌套 例如:京东首页的tab栏,家用电器里面有电视,电视里面有各种牌子,不同牌子又有不同的配色等等,类似这样下一级的参数为上一级的返回值的)
Promise
代码可见:github
概念:
- Promise 是异步编程的一种解决方案,比传统的回调函数和事件更合理、更强大
- ES6的Promise是一个构造函数, 用来生成Promise实例, Promise实例是异步操作管理者
- Promise代表了未来某个将要发生的事件(通常是一个异步操作) 有了Promise对象
- 可以将异步操作以同步的流程表达出来, 避免了层层嵌套的回调函数(回调地狱)
- Promise本身还是在使用回调函数(只不过比回调函数多了一种状态管理)
- promise三个状态:
- 初始化状态 pending
- 成功状态 fullfilled
- 失败状态 rejected
如何使用
因为promise是构造方法,就是一个类,所以说使用的时候需要进行实例化
使用new进行实例化一个Promise 内置有三种状态
promise内置一个参数 参数是回调函数 回调函数中有两个参数 其实都是函数,这两个参数代表着成功状态和失败状态
1
2
3
4
5
6
7
8
9
10
11
12let promise = new Promise((resolve,reject)=>{
// 回调函数中有两个参数,这两个参数都是函数,
console.log(typeof resolve); //function
console.log(typeof reject); //function
// 初始化状态其实就是许诺状态
let num = 10;
if(num>9){
resolve("成功");
}else{
reject("失败")
}
})以上是我们创建的promise
但是创建之后如何使用 现在的promise对象和没写一样 一点作用都没有
使用promise对象进行触发 promise对象中的then方法
then中内置一个或者两个参数 第一个参数是我们定义成功状态的执行方法
第二个参数是我们定义的失败状态的执行方法 也就是说两个参数 都是函数
- 传统写法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19// Promise内置一个参数 参数是回调函数 回调内置两个参数
// 第一个参数是成功状态 第二个参数是失败状态
// 这两个参数其实都是函数 只不过还没有定义
let promise = new Promise((resolve,reject)=>{
// 初始化一个值
let num = 10;
// 判定
if (num > 90){
resolve('成功');
}else{
reject('失败');
}
});
// ps:可以理解为上面的resolve("成功")和reject("失败")为函数的调用,括号里传的为实参;而promise.then里面的两个参数为函数的声明,data和err为形参;
promise.then(data=>{
console.log('执行已经' + data);
},err=>{
console.log('执行出现了' + err);
});- 链式操作(现写法)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29let promise = new Promise((resolve,reject)=>{
// 初始化一个值
let num = 10;
console.log(aaa);
// 判定
if (num > 90){
resolve('成功');
}else{
reject('失败');
}
});
// 我们一般不会在then方法中同时传递两个参数(传统写法),因为如果说传递两个参数 那么不会进行异常处理
// 所以说 失败的状态 我们一般情况下 使用catch状态进行触发 这样经过了异常处理
// 使用then方法触发成功状态 使用catch方法触发失败状态
// 但是注意 catch不是promise的方法 而是then的返回值的方法
// 也就是说 promise对象使用then触发之后 then方法的返回值也是一个promise
//常规写法
let p =promise.then(data=>{
console.log("当前状态为:"+data)
})
p.catch(err=>{
console.log("当前状态为:"+err)
})
// 链式操作
promise.then(data=>{
console.log(data);
}).catch(err=>{
console.log(err);
})- 链式操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37let promise = new Promise((resolve,reject)=>{
if (true){
resolve('一级栏目');
}else{
reject('没出来');
}
})
// 第一个promise中的无论是成功状态还是失败装填的返回值
// 都作为链式操作中第二个promise的resolve(成功)状态
// let p = promise.then(data=>{
// console.log(data);
// return data; //记得return出去
// }).catch(err=>{
// console.log(err);
// return err; //记得return出去
// })
// p.then(data1=>{
// console.log("根据" + data1 + "获取二级栏目");
// })
let p =promise.then(data=>{
console.log(data);
return data;//记得return出去
}).then(data1=>{
console.log("根据"+data1+"获取二级栏目")
return "根据"+data1+"获取二级栏目";//记得return出去
}).then(data2=>{
console.log(data2+"获取三级栏目")
return data3;//记得return出去
})
/*
一级栏目
根据一级栏目获取二级栏目
根据一级栏目获取二级栏目获取三级栏目
*/- Promise.all()
- all方法是处理Promise的方法 他是Promise中的一个静态方法 static
- 也就是说 我们使用的时候 不需要实例化 我们直接使用Promise.all方法就可以
- 它主要的作用是执行所有的promise
- 内置一个参数 参数是一个数组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21let p1 = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('2s钟之后执行p1');
},2000)
})
let p2 = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('3s钟之后执行p2');
},3000)
})
let p3 = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('1s钟之后执行p3');
},1000)
})
// all方法是执行所有的Promise
// 内置一个参数 参数是一个数组
Promise.all([p1,p2,p3]).then(data=>console.log(data));//['2s钟之后执行p1', '3s钟之后执行p2', '1s钟之后执行p3']- Promise.race()
- all方法是处理Promise的方法 他是Promise中的一个静态方法 static
- 也就是说 我们使用的时候 不需要实例化 我们直接使用Promise.race方法就可以
- 他只执行最快的那个promise
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20let p1 = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('2s钟之后执行p1');
},2000)
})
let p2 = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('3s钟之后执行p2');
},3000)
})
let p3 = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('1s钟之后执行p3');
},1000)
})
//race方法是执行最快的Promise
Promise.race([p1,p2,p3]).then(data=>console.log(data));//1s钟之后执行p3
async
概念:
- 真正意义上去解决异步回调的问题,同步流程表达异步操作
- 本质上async 是Promise的语法糖
特点:
- async函数是一个异步函数 但是主要的作用是处理函数内部的异步问题
- 将函数内部的异步代码转化为同步代码 再执行
- 在函数中 如果说程序碰到await 程序会陷入阻塞状态
- 也就是说 在async函数中有await关键字 这个关键字只能出现在async函数中
- 一直到程序执行完成await后面的表达式 程序才会向下进行
async函数主要有三点
函数必须使用async进行修饰
函数的返回值是一个promise
await将异步代码转化为同步代码
await可以直接获取Promise中的resolve状态 并且是异常处理过后的
1
2
3
4
5
6
7
8async function fn(){
let v1 = await "这是第一行代码";
let v2 = await "这是第二行代码";
console.log(v1);
console.log(v2);
}
fn(); //这是第一行代码
//这是第二行代码案例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38function fn1() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('2s钟之后执行p1');
}, 2000)
})
}
function fn2() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('3s钟之后执行p2');
}, 3000)
})
}
function fn3() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('1s钟之后执行p3');
}, 1000)
})
}
// 定义async函数
async function fn() {
let v1 = await fn1();
console.log(v1)
let v2 = await fn2();
console.log(v2)
let v3 = await fn3();
console.log(v3)
}
fn();
/*
2s钟之后执行p1
3s钟之后执行p2
1s钟之后执行p3
*/案例传参:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45const connect = require('./lib/connect');
function fn1(){
return new Promise((resolve,reject)=>{
connect.query('select * from category_first',(err,data)=>{
let result = data[1].first_id;
resolve(result);
})
})
}
function fn2(v){
return new Promise((resolve,reject)=>{
connect.query('select * from category_second where first_id = ?',v,(err,data)=>{
let result = data[0].second_id;
resolve(result);
})
})
}
function fn3(v){
return new Promise((resolve,reject)=>{
connect.query('select * from category_thired where second_id = ?',v,(err,data)=>{
let result = data[0].thired_id;
resolve(result);
})
})
}
function fn4(v){
return new Promise((resolve,reject)=>{
connect.query('select goods_name,goods_id from goods_list where thired_id = ?',v,(err,data)=>{
resolve(data);
})
})
}
async function fn(){
let v1 = await fn1();
let v2 = await fn2(v1);
let v3 = await fn3(v2);
let v4 = await fn4(v3);
console.log(v4);
}
fn();
后续补充
async函数返一个promise对象,当函数执行的时候,一旦遇到await就会先返回,等到触发的异步任务完成,再接着执行后面的语句;(为了验证,看如下代码)
1
2
3
4
5
6
7
8
9async function test(){
await setTimeout(() => {
console.log("我是不是先执行");
}, 1000);
console.log("我什么时候执行")
}
test();
// 我什么时候执行
//(1s后)我是不是最开始执行我们发现,并非我们预想的执行顺序,先打印‘我是不是最开始执行’,而是从下面先开始执行了?其实,这里是有条件的:
如果await后面跟的是promise对象,会将promise异步操作转为同步,等待promise的resolve/reject返回结果,再接着执行函数体后面的语句;
(继续测试如下):
1
2
3
4
5
6
7
8
9
10async function test() {
await new Promise((resolve, reject) => {
setTimeout(() => {
console.log("我是不是先执行");
}, 1000);
})
console.log("我什么时候执行")
}
test();
// (1s后) 我是不是最开始执行我们发现,这跟预期的还是不一样。最下面的 console.log(‘我什么时候执行’)没执行。
原来:
- 如果await的是一个promise对象,那么要等待这个对象解析完成;
- 如果没有resolve/reject,那么后面的内容就不再执行;
我们再修改,测试如下:
1
2
3
4
5
6
7
8
9
10
11
12async function test() {
await new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
console.log("我是不是先执行");
}, 1000);
})
console.log("我什么时候执行")
}
test();
// (1s后) 我是不是最开始执行
// (1s后,和上面同时) 我什么时候执行
- 小结论
所以我们可以得出一个结论!
await后面如果
不是一个promise对象
,那么它就按正常的js顺序执行
,先执行同步代码,当主线程空闲了,再去执行异步队列的任务;await后面如果
是promise对象
那么要等待这个对象解析完成,如果没有resolve或者reject
,那么后面的内容就不会执行
;如果有resolve或者reject
,那么后面的内容正常执行
。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15async function test() {
console.log("async1");
await new Promise((resolve, reject) => {
resolve()
});
console.log('async2');
};
//等价于
async function test() {
console.log("async1");
await new Promise((resolve, reject) => {
resolve()
}).then(() => console.log('async2'));
};
- await的返回值
1 | async function test() { |
我们发现:
- await的返回值就是Promise的resolve/reject返回结果,然后通过async函数return出来,相当于Promise.resolve(“返回结果”);