十、promise的使用
Promise用来做什么
用来解决回调地狱。回调地狱也就是回调函数中嵌套了回调函数,代码阅读性低。
例如如下代码:
1 | const fs = require("fs"); |
在读完A文件后读取B文件在读取C文件。
Promise工作流程
Promise
对象是一个构造函数,用来生成Promise
实例。Promise
构造函数接收一个函数作为参数。这个参数的函数又有两个参数,这两个参数分别是resolve
和reject
。这两个参数也是函数,由JavaScript引擎提供。
- 异步操作成功后调用
resolve()
方法,他内部调用了then()
里面的第一个参数函数。 - 异步操作失败后调用
reject()
方法,他内部调用了then()
里面的第二个参数函数。
1 | const fs = require("fs"); |
Promise原理
Promise
对象代表一个异步操作,有三种状态:pending
(进行中)、fulfilled
(已成功)和rejected
(已失败)。
只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。
Promise
对象的状态改变,只有两种可能:
异步操作成功
从
pending
变为fulfilled
异步操作失败
从
pending
变为rejected
Promise对象的特点以及封装
特点
创建
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
26const fs = require("fs");
let p = new Promise((resolve, reject) => {
console.log("promise - test");
// 读文件
fs.readFile(`${__dirname}/etc/a.txt`, "utf-8", (err, data) => {
if (!err) {
// 读取成功
resolve(data); // 调用此方法
} else {
// 读取失败
reject(err); // 调用此方法
}
});
});
p.then(
(data) => {
console.log(data);
},
(err) => {
console.log(err);
}
);
console.log("promise end- test");因此不要在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
26const fs = require("fs");
function getPromise(fileName) {
return new Promise((resolve, reject) => {
// 读文件
fs.readFile(`${__dirname}/etc/${fileName}.txt`, "utf-8", (err, data) => {
if (!err) {
// 读取成功
resolve(data); // 调用此方法
} else {
// 读取失败
reject(err); // 调用此方法
}
});
});
}
getPromise("a").then(
(data) => {
console.log(data);
},
(err) => {
console.log(err);
}
);
解决回调地狱
让异步操作的本质实际上就是在异步操作成功后的回调函数里返回另外的Promise
,在执行另一个then
方法。
1 | const fs = require("fs"); |
Ajax也会返回Promise对象
1 |
|
catch()方法
此方法能够抓取错误,在最后一个then()
方法后边在加一个catch
方法。无论哪一个出错都会抓取到错误。
1 | getPromise("a") |
all()方法
Promise.all()
方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
1 | let p1 = getPromise("a"); |
此方法要求每一个小的
Promise
都要成功,只要有一个失败都会导致整个的Promise
错误。
race()方法
Promise.race()
方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。
此方法与all
方法的区别是只要有一个成功即成功。
1 | let p1 = getPromise("a"); |
Module的语法
使用模块的好处
- 避免变量污染,命名冲突
- 提供代码的复用率、维护性
- 依赖关系管理
export
命令:用于规定模块对外的接口- 外部能够读取模块内部的某个变量、函数、类
- 使用as关键字重命名
- 该命令可以出现在模块的任何位置,只要用于模块顶层即可。除了块作用域内
import
命令:用于输入其他模块提供的功能- 变量、函数
- 使用as关键字
- 输入的变量都是只读的
- import命令具有提升效果
注意:module是静态导入,因此不能使用表达式和变量那些运行时才能知道的结果的变量。
通过export
导出
导出变量
逐个导出
1
2export let name = "张三";
export let age = 12;批量导出
1
2
3let name = "张三";
let age = 23;
export { name, age };
导出函数
逐个导出
1
2
3export function addFn(a, b) {
console.log(a + b);
}批量导出
1
2
3
4
5
6
7function addFn1(a, b) {
console.log(a + b);
}
function addFn2(a, b) {
console.log(a + b);
}
export { addFn1, addFn2 };
通过import
导入
导入的变量只能读不能修改。
导入变量
直接导入
1
import { name, age } from "./09-export.js";
设置别名导入
1
import { name as myName, age } from "./09-export.js";
导入对象
对象中的属性可以修改。
1
2
3
4import { obj } from "./09-export.js";
console.log(obj);
obj.age = 78;
console.log(obj);
HTML
引入模块
在HTML引入
1 | <script type="module" src="./10-import.js"></script> |
浏览器加载 ES6 模块,也使用<script>
标签,但是要加入type="module"
属性。
其他用法
整体加载
1
2
3import * as obj from "./export.js";
console.log("fn1", obj.fn1(5));
console.log("fn1", obj.fn2(5));默认导入
正常输出导入时,要加
{}
。默认输出,导入时不要加{}
一个模块中只能由一个
export default
1
2
3export default function () {
console.log("temp");
}1
import myfn from "./export.js";
复合写法
在上面的三个文件中,import.js
需要使用export.hs
中的变量,而export.js
又需要使用public.js
中的变量。此时可以使用复合写法。
public.js
1 | export let number1 = 10; |
export.js
1 | // 导入public.js的number1变量 |
import.js
1 | import { number1 } from "./export.js"; |