ES6-promise

在JavaScript的世界中,所有代码都是单线程执行的。由于这个“缺陷”,导致 JavaScript的所有网络操作,浏览器事件,都必须是异步执行。异步执行可以用回调函数实现。一旦有一连串的ajax请求a,b,c,d….后面的请求依赖前面的请求结果,就需要层层嵌套。这种缩进和层层嵌套的方式,非常容易造成上下文代码混乱,我们不得不非常小心翼翼处理内层函数与外层函数的数据,一旦内层函数使用了上层函数的变量,这种混乱程度就会加剧…总之,这种层叠上下文的层层嵌套方式,着实增加了神经的紧张程度


先理解一下什么是回调函数

什么是函数?

函数是在其中有一组代码的逻辑构件,用来执行特定任务。实际上为了易于调试和维护,函数允许以更有组织的方式去编写代码。函数还允许代码重用。

你只需定义一次函数,然后在需要时去调用它,而不必一次又一次地编写相同的代码。

回调函数(callback)

首先回调函数就是一个函数,形式上和其他函数没有半点区别.
举个例子:

一般函数:function a(int a, String b):接收的参数是一般类型.

特殊函数:function b(function c):接收的参数是一个函数,c这个函数就叫回调函数.

你也可以这么理解:本质区别是,一般一个函数调用另一个函数,被调用的函数是出现在方法体当中,而回调函数比较特殊,它是出现在参数列表当中.也就是说,当调用的时候,需要从其他地方拿到这个(回调)函数,以参数的形式传入.
示例

异步操作

同步和异步区别?

举个栗子,煮开水,同步就是把水放上去烧,得一直等水开,中途不能做其他事情。而异步,则是把水放上去烧,让水在烧,你可以玩手机看电视,等水开了把火关掉。同样的,代码中也是一样,同步是现在发生的,异步是未来某个时刻发生的。

JS 运行机制

先介绍下JavaScript运行机制,因为JS 是单线程运行的,所以这意味着两段代码不能同时运行,而是必须一个接一个地运行,所以,在同步代码执行过程中,异步代码是不执行的。只有等同步代码执行结束后,异步代码才会被添加到事件队列中。

JS 中异步有几种?

JS 中异步操作还挺多的,常见的分以下几种:

1
2
3
4
setTimeout (setInterval)
AJAX
Promise
async/await

Promise

状态的特点
Promise 异步操作有三种状态:pending(进行中)、fulfilled(已成功)和 rejected(已失败)。除了异步操作的结果,任何其他操作都无法改变这个状态。

Promise 对象只有:从 pending 变为 fulfilled 和从 pending 变为 rejected 的状态改变。只要处于 fulfilled 和 rejected ,状态就不会再变了即 resolved(已定型)。
语法:

1
new Promise( function(resolve, reject) {...});

Promise 对象是由关键字 new 及其构造函数来创建的。这个“处理器函数”接受两个函数 resolve 和 reject 作为其参数。当异步任务顺利完成且返回结果值时,会调用 resolve 函数;而当异步任务失败且返回失败原因(通常是一个错误对象)时,会调用reject 函数。

new Promise 返回一个 promise 对象,在遇到 resolve 或 reject之前,状态一直是pending,如果调用 resolve 方法,状态变为 fulfilled,如果调用了 reject 方法,状态变为 rejected。

例子:
案例:用户登录,并展示该用户的各科成绩。在页面发送两次请求:
1.查询当前用户信息
2.按照当前用户的id查出他的课程
3.按照当前课程id查出分数
分析:此时后台应该提供三个接口,一个提供用户查询接口,一个提供科目的接口,一个提供各科成绩的接口,为了渲染方便,最好响应json数据。在这里就不编写后台接口了,而是提供三个json文件,直接提供json数据,模拟后台接口。
数据如图

  1. ajax发送请求获取数据方法
    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
    <!-- 导入JQuery -->
    <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
    <script>
    $.ajax({
    url: "mock/user.json",
    success(data) {
    console.log("查询用户:", data);
    $.ajax({
    url: `mock/user_corse_${data.id}.json`,
    success(data) {
    console.log("查询到课程:", data);
    $.ajax({
    url: `mock/corse_score_${data.id}.json`,
    success(data) {
    console.log("查询到分数:", data);
    },
    error(error) {
    console.log("出现异常了:" + error);
    },
    });
    },
    error(error) {
    console.log("出现异常了:" + error);
    },
    });
    },
    error(error) {
    console.log("出现异常了:" + error);
    },
    });
    </script>
  2. Promise方法
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
1.primise可以封装异步操作
let p = new Promise((resolve, reject) => {
//1.异步操作
$.ajax({
url: "user.json",
success: function (data) {
console.log("查询用户成功:", data);
resolve(data); //成功了
},
error: function (err) {
reject(err); //失败了
},
});
});
//then 方法接收两个函数作为参数,第一个参数是 Promise 执行成功时的回调,
//第二个参数是 Promise 执行失败时的回调,两个函数只会有一个被调用。
// obj对象是上一步传下来的对象
p.then((obj) => {
//p.then调用完以后还会返回一个promise对象
return new Promise((resolve, reject) => {
$.ajax({
url: `usr_corse_${obj.id}.json`,
success: function (data) {
console.log("查询用户课程成功:", data);
resolve(data);
},
error: function (err) {
reject(err);
},
});
});
}).then((data) => {
// data是上一步的data
console.log("上一步的结果", data);
$.ajax({
url: `corse_score_${data.id}.json`,
success: function (data) {
console.log("查询课程得分成功:", data);
},
error: function (err) {},
});
});
  1. 简化Promise,抽取方法
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
function get(url, data) {
// new Promise封装一个异步操作
return new Promise((resolve, reject) => {
$.ajax({
url: url,
data: data,
success: function (data) {
resolve(data);
},
error: function (err) {
reject(err);
},
});
});
}
// 发送一个get请求,由于get封装的是Promise方法所以可以调用.then方法
get("user.json")
.then((data) => {
console.log("用户查询成功:", data);
return get(`usr_corse_${data.id}.json`);
})
.then((data) => {
console.log("课程查询成功:", data);
return get(`corse_score_${data.id}.json`);
})
.then((data) => {
console.log("课程成绩查询成功:", data);
})
.catch((err) => {
console.log("数据异常", err);
});

Promise.all

Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。

1
Promise.all([promise1, promise2]).then(success1, fail1)

promise1和promise2都成功才会调用success1

Promise.race

Promise.race()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。

1
Promise.race([promise1, promise2]).then(success1, fail1)

promise1和promise2只要有一个成功就会调用success1

总结

1.Promise的作用
主要用于异步计算
可以将异步操作队列化,按照期望的顺序执行,返回符合预期的结果
可以在对象之间传递和操作promise,帮助我们处理队列

2.理解
promise是一个对象,对象和函数的区别就是对象可以保存状态,函数不可以(闭包除外)
并未剥夺函数return的能力,因此无需层层传递callback,进行回调获取数据
代码风格,容易理解,便于维护
多个异步等待合并便于解决

参考链接

理解 JavaScript 异步操作
菜鸟教程