异步处理
回调函数
概念:一个函数作为另一个函数的参数;先执行一个函数,把他的执行结果作为另一个函数的参数;
弊端:会产生回调地狱。(回调函数的层层嵌套 例如:京东首页的 tab 栏,家用电器里面有电视,电视里面有各种牌子,不同牌子又有不同的配色等等,类似这样下一级的参数为上一级的返回值的)
Promise
代码可见:github
概念:
- Promise 是异步编程的一种解决方案,比传统的回调函数和事件更合理、更强大
- ES6 的 Promise 是一个构造函数,用来生成 Promise 实例,Promise 实例是异步操作管理者
- Promise 代表了未来某个将要发生的事件 (通常是一个异步操作) 有了 Promise 对象
- 可以将异步操作以同步的流程表达出来,避免了层层嵌套的回调函数 (回调地狱)
- Promise 本身还是在使用回调函数 (只不过比回调函数多了一种状态管理)
- promise 三个状态:
- 初始化状态 pending
- 成功状态 fullfilled
- 失败状态 rejected
如何使用
因为 promise 是构造方法,就是一个类,所以说使用的时候需要进行实例化
使用 new 进行实例化一个 Promise 内置有三种状态
promise 内置一个参数 参数是回调函数 回调函数中有两个参数 其实都是函数,这两个参数代表着成功状态和失败状态
js1
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 中内置一个或者两个参数 第一个参数是我们定义成功状态的执行方法
第二个参数是我们定义的失败状态的执行方法 也就是说两个参数 都是函数
- 传统写法
js1
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);
});- 链式操作 (现写法)
js1
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);
})- 链式操作
js1
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
- 内置一个参数 参数是一个数组
js1
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
js1
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 状态 并且是异常处理过后的
js1
2
3
4
5
6
7
8async function fn(){
let v1 = await "这是第一行代码";
let v2 = await "这是第二行代码";
console.log(v1);
console.log(v2);
}
fn(); //这是第一行代码
//这是第二行代码案例:
js1
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
*/案例传参:
js1
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 就会先返回,等到触发的异步任务完成,再接着执行后面的语句;(为了验证,看如下代码)
js1
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 返回结果,再接着执行函数体后面的语句;
(继续测试如下):
js1
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,那么后面的内容就不再执行;
我们再修改,测试如下:
js1
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
,那么后面的内容正常执行
。js1
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 (“返回结果”);