From 50392947b6838829432e25f898d3e389d2eb8a2b Mon Sep 17 00:00:00 2001 From: YuanJin <277739025@qq.com> Date: Mon, 1 Nov 2021 11:35:48 +0800 Subject: [PATCH] init --- .gitignore | 1 + 01. Promise基础/1.js | 69 ++++ 01. Promise基础/2.js | 23 ++ 01. Promise基础/3.js | 33 ++ 01. Promise基础/笔记.md | 206 ++++++++++++ 01. Promise基础/练习题/p1.js | 12 + 01. Promise基础/练习题/p2.html | 26 ++ 01. Promise基础/练习题/p3.html | 28 ++ 01. Promise基础/练习题/p4.js | 16 + 01. Promise基础/练习题参考答案/p1.js | 21 ++ 01. Promise基础/练习题参考答案/p2.html | 59 ++++ 01. Promise基础/练习题参考答案/p3.html | 39 +++ 01. Promise基础/练习题参考答案/p4.js | 18 ++ 02. Promise的链式调用/1.js | 5 + 02. Promise的链式调用/2.js | 12 + 02. Promise的链式调用/3.js | 52 ++++ 02. Promise的链式调用/笔记.md | 103 ++++++ 02. Promise的链式调用/练习题.1/p1.js | 14 + 02. Promise的链式调用/练习题.1/p2.js | 19 ++ 02. Promise的链式调用/练习题.1/p3.js | 19 ++ 02. Promise的链式调用/练习题.1/p4.js | 16 + 02. Promise的链式调用/练习题.1/p5.js | 18 ++ 02. Promise的链式调用/练习题/p1.js | 21 ++ 02. Promise的链式调用/练习题/p2.js | 15 + 02. Promise的链式调用/练习题/p3.js | 15 + 02. Promise的链式调用/练习题/p4.js | 16 + 02. Promise的链式调用/练习题/p5.js | 18 ++ 03. Promise的静态方法/1.js | 50 +++ 03. Promise的静态方法/2.js | 16 + 03. Promise的静态方法/笔记.md | 95 ++++++ 03. Promise的静态方法/练习题/p1.js | 30 ++ 03. Promise的静态方法/练习题参考答案/p1.html | 12 + 03. Promise的静态方法/练习题参考答案/p1.js | 62 ++++ 04. async和await/1.js | 29 ++ 04. async和await/2.js | 45 +++ 04. async和await/笔记.md | 177 +++++++++++ 04. async和await/练习题/p1.html | 13 + 04. async和await/练习题/p2.js | 7 + 04. async和await/练习题参考答案/p1.html | 19 ++ 04. async和await/练习题参考答案/p2.js | 33 ++ 05. Promise相关面试题/1.js | 11 + 05. Promise相关面试题/2.js | 14 + 05. Promise相关面试题/3.js | 16 + 05. Promise相关面试题/4.js | 14 + 05. Promise相关面试题/5.js | 12 + 05. Promise相关面试题/6.js | 23 ++ 05. Promise相关面试题/7.js | 1 + 05. Promise相关面试题/8.js | 28 ++ 05. Promise相关面试题/9.js | 24 ++ 05. Promise相关面试题/笔记.md | 311 +++++++++++++++++++ 06. 手写Promise/MyPromise.js | 287 +++++++++++++++++ 06. 手写Promise/说明.md | 3 + 知识图谱.drawio | 64 ++++ 53 files changed, 2290 insertions(+) create mode 100644 .gitignore create mode 100644 01. Promise基础/1.js create mode 100644 01. Promise基础/2.js create mode 100644 01. Promise基础/3.js create mode 100644 01. Promise基础/笔记.md create mode 100644 01. Promise基础/练习题/p1.js create mode 100644 01. Promise基础/练习题/p2.html create mode 100644 01. Promise基础/练习题/p3.html create mode 100644 01. Promise基础/练习题/p4.js create mode 100644 01. Promise基础/练习题参考答案/p1.js create mode 100644 01. Promise基础/练习题参考答案/p2.html create mode 100644 01. Promise基础/练习题参考答案/p3.html create mode 100644 01. Promise基础/练习题参考答案/p4.js create mode 100644 02. Promise的链式调用/1.js create mode 100644 02. Promise的链式调用/2.js create mode 100644 02. Promise的链式调用/3.js create mode 100644 02. Promise的链式调用/笔记.md create mode 100644 02. Promise的链式调用/练习题.1/p1.js create mode 100644 02. Promise的链式调用/练习题.1/p2.js create mode 100644 02. Promise的链式调用/练习题.1/p3.js create mode 100644 02. Promise的链式调用/练习题.1/p4.js create mode 100644 02. Promise的链式调用/练习题.1/p5.js create mode 100644 02. Promise的链式调用/练习题/p1.js create mode 100644 02. Promise的链式调用/练习题/p2.js create mode 100644 02. Promise的链式调用/练习题/p3.js create mode 100644 02. Promise的链式调用/练习题/p4.js create mode 100644 02. Promise的链式调用/练习题/p5.js create mode 100644 03. Promise的静态方法/1.js create mode 100644 03. Promise的静态方法/2.js create mode 100644 03. Promise的静态方法/笔记.md create mode 100644 03. Promise的静态方法/练习题/p1.js create mode 100644 03. Promise的静态方法/练习题参考答案/p1.html create mode 100644 03. Promise的静态方法/练习题参考答案/p1.js create mode 100644 04. async和await/1.js create mode 100644 04. async和await/2.js create mode 100644 04. async和await/笔记.md create mode 100644 04. async和await/练习题/p1.html create mode 100644 04. async和await/练习题/p2.js create mode 100644 04. async和await/练习题参考答案/p1.html create mode 100644 04. async和await/练习题参考答案/p2.js create mode 100644 05. Promise相关面试题/1.js create mode 100644 05. Promise相关面试题/2.js create mode 100644 05. Promise相关面试题/3.js create mode 100644 05. Promise相关面试题/4.js create mode 100644 05. Promise相关面试题/5.js create mode 100644 05. Promise相关面试题/6.js create mode 100644 05. Promise相关面试题/7.js create mode 100644 05. Promise相关面试题/8.js create mode 100644 05. Promise相关面试题/9.js create mode 100644 05. Promise相关面试题/笔记.md create mode 100644 06. 手写Promise/MyPromise.js create mode 100644 06. 手写Promise/说明.md create mode 100644 知识图谱.drawio diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ffcf992 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.dio \ No newline at end of file diff --git a/01. Promise基础/1.js b/01. Promise基础/1.js new file mode 100644 index 0000000..bf9272c --- /dev/null +++ b/01. Promise基础/1.js @@ -0,0 +1,69 @@ +// 向某位女生发送一则表白短信 +// name: 女神的姓名 +// onFulffiled: 成功后的回调 +// onRejected: 失败后的回调 +function sendMessage(name, onFulffiled, onRejected) { + // 模拟 发送表白短信 + console.log( + `邓哥 -> ${name}:最近有谣言说我喜欢你,我要澄清一下,那不是谣言😘` + ); + console.log(`等待${name}回复......`); + // 模拟 女神回复需要一段时间 + setTimeout(() => { + // 模拟 有10%的几率成功 + if (Math.random() <= 0.1) { + // 成功,调用 onFuffiled,并传递女神的回复 + onFulffiled(`${name} -> 邓哥:我是九,你是三,除了你还是你😘`); + } else { + // 失败,调用 onRejected,并传递女神的回复 + onRejected(`${name} -> 邓哥:你是个好人😜`); + } + }, 1000); +} + +// 首先向 李建国 发送消息 +sendMessage( + '李建国', + (reply) => { + // 如果成功了,输出回复的消息后,结束 + console.log(reply); + }, + (reply) => { + // 如果失败了,输出回复的消息后,向 王富贵 发送消息 + console.log(reply); + sendMessage( + '王富贵', + (reply) => { + // 如果成功了,输出回复的消息后,结束 + console.log(reply); + }, + (reply) => { + // 如果失败了,输出回复的消息后,向 周聚财 发送消息 + console.log(reply); + sendMessage( + '周聚财', + (reply) => { + // 如果成功了,输出回复的消息后,结束 + console.log(reply); + }, + (reply) => { + // 如果失败了,输出回复的消息后,向 刘人勇 发送消息 + console.log(reply); + sendMessage( + '刘人勇', + (reply) => { + // 如果成功了,输出回复的消息后,结束 + console.log(reply); + }, + (reply) => { + // 如果失败了,就彻底没戏了 + console.log(reply); + console.log('邓哥命犯天煞孤星,注定孤独终老!!'); + } + ); + } + ); + } + ); + } +); diff --git a/01. Promise基础/2.js b/01. Promise基础/2.js new file mode 100644 index 0000000..9234c97 --- /dev/null +++ b/01. Promise基础/2.js @@ -0,0 +1,23 @@ +const pro = new Promise((resolve, reject) => { + console.log('开始百米短跑'); + const duration = Math.floor(Math.random() * 5000); + + setTimeout(() => { + if (Math.random() < 0.5) { + // 成功 + resolve(duration); // 将任务从挂起->完成 + } else { + // 失败,脚扭伤了 + reject('脚扭伤了!'); + } + }, duration); +}); + +pro.then( + (data) => { + console.log('on yeah! 我跑了', data, '秒'); + }, + (reason) => { + console.log('不好意思,', reason); + } +); diff --git a/01. Promise基础/3.js b/01. Promise基础/3.js new file mode 100644 index 0000000..4345d8c --- /dev/null +++ b/01. Promise基础/3.js @@ -0,0 +1,33 @@ +// 向某位女生发送一则表白短信 +// name: 女神的姓名 +// onFulffiled: 成功后的回调 +// onRejected: 失败后的回调 +function sendMessage(name) { + return new Promise((resolve, reject) => { + // 模拟 发送表白短信 + console.log( + `邓哥 -> ${name}:最近有谣言说我喜欢你,我要澄清一下,那不是谣言😘` + ); + console.log(`等待${name}回复......`); + // 模拟 女神回复需要一段时间 + setTimeout(() => { + // 模拟 有10%的几率成功 + if (Math.random() <= 0.1) { + // 成功,调用 onFuffiled,并传递女神的回复 + resolve(`${name} -> 邓哥:我是九,你是三,除了你还是你😘`); + } else { + // 失败,调用 onRejected,并传递女神的回复 + reject(`${name} -> 邓哥:你是个好人😜`); + } + }, 1000); + }); +} + +sendMessage('李建刚').then( + (reply) => { + console.log('成功!', reply); + }, + (reply) => { + console.log('失败!', reply); + } +); diff --git a/01. Promise基础/笔记.md b/01. Promise基础/笔记.md new file mode 100644 index 0000000..1bede83 --- /dev/null +++ b/01. Promise基础/笔记.md @@ -0,0 +1,206 @@ +> 本节课的任务: +> +> 1. 理解Promise A+规范的基本概念 +> 2. 学会创建Promise +> 3. 学会针对Promise进行后续处理 + +# 邓哥的烦恼 + +邓哥心中有很多女神,他今天下定决心,要向这些女神表白,他认为,只要女神够多,根据概率学原理,总有一个会接收他 + +稳妥起见,邓哥决定使用**串行**的方式进行表白:先给第1位女神发送短信,然后等待女神的回应,如果成功了,就结束,如果失败了,则再给第2位女神发送短信,依次类推 + +![image-20210618150543263](http://mdrs.yuanjin.tech/img/20210618150543.png) + +邓哥的女神一共有4位,名字分别是:李建国、王富贵、周聚财、刘人勇 + +发短信是一个重复性的劳动,邓哥是个程序员,因此决定用函数封装这个动作 + +```js +// 向某位女生发送一则表白短信 +// name: 女神的姓名 +// onFulffiled: 成功后的回调 +// onRejected: 失败后的回调 +function sendMessage(name, onFulffiled, onRejected) { + // 模拟 发送表白短信 + console.log( + `邓哥 -> ${name}:最近有谣言说我喜欢你,我要澄清一下,那不是谣言😘` + ); + console.log(`等待${name}回复......`); + // 模拟 女神回复需要一段时间 + setTimeout(() => { + // 模拟 有10%的几率成功 + if (Math.random() <= 0.1) { + // 成功,调用 onFuffiled,并传递女神的回复 + onFulffiled(`${name} -> 邓哥:我是九,你是三,除了你还是你😘`); + } else { + // 失败,调用 onRejected,并传递女神的回复 + onRejected(`${name} -> 邓哥:你是个好人😜`); + } + }, 1000); +} +``` + +有了这个函数后,邓哥于是开始编写程序发送短信了 + +```js +// 首先向 李建国 发送消息 +sendMessage( + '李建国', + (reply) => { + // 如果成功了,输出回复的消息后,结束 + console.log(reply); + }, + (reply) => { + // 如果失败了,输出回复的消息后,向 王富贵 发送消息 + console.log(reply); + sendMessage( + '王富贵', + (reply) => { + // 如果成功了,输出回复的消息后,结束 + console.log(reply); + }, + (reply) => { + // 如果失败了,输出回复的消息后,向 周聚财 发送消息 + console.log(reply); + sendMessage( + '周聚财', + (reply) => { + // 如果成功了,输出回复的消息后,结束 + console.log(reply); + }, + (reply) => { + // 如果失败了,输出回复的消息后,向 刘人勇 发送消息 + console.log(reply); + sendMessage( + '刘人勇', + (reply) => { + // 如果成功了,输出回复的消息后,结束 + console.log(reply); + }, + (reply) => { + // 如果失败了,就彻底没戏了 + console.log(reply); + console.log('邓哥命犯天煞孤星,注定孤独终老!!'); + } + ); + } + ); + } + ); + } +); +``` + +该程序完成后,邓哥内心是崩溃的 + +这一层一层的回调嵌套,形成了传说中的「**回调地狱 callback hell**」 + +邓哥是个完美主义者,怎么能忍受这样的代码呢? + +要解决这样的问题,需要Promise出马 + +# Promise规范 + +Promise是一套专门处理异步场景的规范,它能有效的避免回调地狱的产生,使异步代码更加清晰、简洁、统一 + +这套规范最早诞生于前端社区,规范名称为[Promise A+](https://promisesaplus.com/) + +该规范出现后,立即得到了很多开发者的响应 + +Promise A+ 规定: + +1. 所有的异步场景,都可以看作是一个异步任务,每个异步任务,在JS中应该表现为一个**对象**,该对象称之为**Promise对象**,也叫做任务对象 + + image-20210618154556558 + +2. 每个任务对象,都应该有两个阶段、三个状态 + + image-20210618155145355 + + 根据常理,它们之间存在以下逻辑: + + - 任务总是从未决阶段变到已决阶段,无法逆行 + - 任务总是从挂起状态变到完成或失败状态,无法逆行 + - 时间不能倒流,历史不可改写,任务一旦完成或失败,状态就固定下来,永远无法改变 + +3. `挂起->完成`,称之为`resolve`;`挂起->失败`称之为`reject`。任务完成时,可能有一个相关数据;任务失败时,可能有一个失败原因。 + + ![image-20210618160538875](http://mdrs.yuanjin.tech/img/20210618160538.png) + +4. 可以针对任务进行后续处理,针对完成状态的后续处理称之为onFulfilled,针对失败的后续处理称之为onRejected + + ![image-20210618161125894](http://mdrs.yuanjin.tech/img/20210618161125.png) + +# Promise API + +ES6提供了一套API,实现了Promise A+规范 + +基本使用如下: + +```js +// 创建一个任务对象,该任务立即进入 pending 状态 +const pro = new Promise((resolve, reject) => { + // 任务的具体执行流程,该函数会立即被执行 + // 调用 resolve(data),可将任务变为 fulfilled 状态, data 为需要传递的相关数据 + // 调用 reject(reason),可将任务变为 rejected 状态,reason 为需要传递的失败原因 +}); + +pro.then( + (data) => { + // onFulfilled 函数,当任务完成后,会自动运行该函数,data为任务完成的相关数据 + }, + (reason) => { + // onRejected 函数,当任务失败后,会自动运行该函数,reason为任务失败的相关原因 + } +); +``` + +# 邓哥的解决方案 + +学习了ES6的Promise后,邓哥决定对`sendMessage`函数进行改造,改造结果如下: + +```js +// 向某位女生发送一则表白短信 +// name: 女神的姓名 +// 该函数返回一个任务对象 +function sendMessage(name) { + return new Promise((resolve, reject) => { + // 模拟 发送表白短信 + console.log( + `邓哥 -> ${name}:最近有谣言说我喜欢你,我要澄清一下,那不是谣言😘` + ); + console.log(`等待${name}回复......`); + // 模拟 女神回复需要一段时间 + setTimeout(() => { + // 模拟 有10%的几率成功 + if (Math.random() <= 0.1) { + // 成功,调用 resolve,并传递女神的回复 + resolve(`${name} -> 邓哥:我是九,你是三,除了你还是你😘`); + } else { + // 失败,调用 reject,并传递女神的回复 + reject(`${name} -> 邓哥:你是个好人😜`); + } + }, 1000); + }); +} +``` + +之后,就可以使用该函数来发送消息了 + +```js +sendMessage('李建国').then( + (reply) => { + // 女神答应了,输出女神的回复 + console.log(reply); + }, + (reason) => { + // 女神拒绝了,输出女神的回复 + console.log(reason); + } +); +``` + +> 至此,回调地狱的问题仍然没能解决 +> +> 要解决回调地狱,还需要进一步学习Promise的知识 \ No newline at end of file diff --git a/01. Promise基础/练习题/p1.js b/01. Promise基础/练习题/p1.js new file mode 100644 index 0000000..729673d --- /dev/null +++ b/01. Promise基础/练习题/p1.js @@ -0,0 +1,12 @@ +//1. 完成下面的函数 + +/** + * 延迟一段指定的时间 + * @param {Number} duration 等待的时间 + * @returns {Promise} 返回一个任务,该任务在指定的时间后完成 + */ +function delay(duration) {} + +// 2. 按照要求,调用delay函数,完成程序 + +// 利用delay函数,等待1秒钟,输出:finish diff --git a/01. Promise基础/练习题/p2.html b/01. Promise基础/练习题/p2.html new file mode 100644 index 0000000..743c413 --- /dev/null +++ b/01. Promise基础/练习题/p2.html @@ -0,0 +1,26 @@ + + + + + + + Document + + +
+

+ + + diff --git a/01. Promise基础/练习题/p3.html b/01. Promise基础/练习题/p3.html new file mode 100644 index 0000000..884419f --- /dev/null +++ b/01. Promise基础/练习题/p3.html @@ -0,0 +1,28 @@ + + + + + + + Document + + + + + + + diff --git a/01. Promise基础/练习题/p4.js b/01. Promise基础/练习题/p4.js new file mode 100644 index 0000000..caf5cdf --- /dev/null +++ b/01. Promise基础/练习题/p4.js @@ -0,0 +1,16 @@ +// 下面的任务最终状态是什么,相关的数据或失败原因是什么,最终输出什么 + +new Promise((resolve, reject) => { + console.log('任务开始'); + resolve(1); + reject(2); + resolve(3); + console.log('任务结束'); +}); + +new Promise((resolve, reject) => { + console.log('任务开始'); + resolve(1); + resolve(2); + console.log('任务结束'); +}); diff --git a/01. Promise基础/练习题参考答案/p1.js b/01. Promise基础/练习题参考答案/p1.js new file mode 100644 index 0000000..76401fd --- /dev/null +++ b/01. Promise基础/练习题参考答案/p1.js @@ -0,0 +1,21 @@ +//1. 完成下面的函数 + +/** + * 延迟一段指定的时间 + * @param {Number} duration 等待的时间 + * @returns {Promise} 返回一个任务,该任务在指定的时间后完成 + */ +function delay(duration) { + return new Promise((resolve) => { + setTimeout(() => { + resolve(); + }, duration); + }); +} + +// 2. 按照要求,调用delay函数,完成程序 + +// 利用delay函数,等待1秒钟,输出:finish +delay(1000).then(() => { + console.log('finish'); +}); diff --git a/01. Promise基础/练习题参考答案/p2.html b/01. Promise基础/练习题参考答案/p2.html new file mode 100644 index 0000000..36ade7e --- /dev/null +++ b/01. Promise基础/练习题参考答案/p2.html @@ -0,0 +1,59 @@ + + + + + + + Document + + +
+

+ + + diff --git a/01. Promise基础/练习题参考答案/p3.html b/01. Promise基础/练习题参考答案/p3.html new file mode 100644 index 0000000..dc520ca --- /dev/null +++ b/01. Promise基础/练习题参考答案/p3.html @@ -0,0 +1,39 @@ + + + + + + + Document + + + + + + + diff --git a/01. Promise基础/练习题参考答案/p4.js b/01. Promise基础/练习题参考答案/p4.js new file mode 100644 index 0000000..413ef23 --- /dev/null +++ b/01. Promise基础/练习题参考答案/p4.js @@ -0,0 +1,18 @@ +// 下面的任务最终状态是什么,相关的数据或失败原因是什么,最终输出什么 + +const pro1 = new Promise((resolve, reject) => { + console.log('任务开始'); + resolve(1); + reject(2); // 无效 + resolve(3); // 无效 + console.log('任务结束'); +}); + +console.log(pro1); + +// new Promise((resolve, reject) => { +// console.log('任务开始'); +// resolve(1); +// resolve(2); // 无效 +// console.log('任务结束'); +// }); diff --git a/02. Promise的链式调用/1.js b/02. Promise的链式调用/1.js new file mode 100644 index 0000000..4a425a5 --- /dev/null +++ b/02. Promise的链式调用/1.js @@ -0,0 +1,5 @@ +new Promise((resolve, reject) => { + reject(new Error('abc')); +}).catch((err) => { + console.log('失败了!!', err); +}); diff --git a/02. Promise的链式调用/2.js b/02. Promise的链式调用/2.js new file mode 100644 index 0000000..35831ca --- /dev/null +++ b/02. Promise的链式调用/2.js @@ -0,0 +1,12 @@ +const pro1 = new Promise((resolve, reject) => { + console.log('学习'); + resolve(); +}); + +const pro2 = pro1.then(() => { + return new Promise((resolve, reject) => {}); +}); + +setTimeout(() => { + console.log(pro2); +}, 1000); diff --git a/02. Promise的链式调用/3.js b/02. Promise的链式调用/3.js new file mode 100644 index 0000000..12d4ffa --- /dev/null +++ b/02. Promise的链式调用/3.js @@ -0,0 +1,52 @@ +// 向某位女生发送一则表白短信 +// name: 女神的姓名 +// 返回:Promise +function sendMessage(name) { + return new Promise((resolve, reject) => { + // 模拟 发送表白短信 + console.log( + `邓哥 -> ${name}:最近有谣言说我喜欢你,我要澄清一下,那不是谣言😘` + ); + console.log(`等待${name}回复......`); + // 模拟 女神回复需要一段时间 + setTimeout(() => { + // 模拟 有10%的几率成功 + if (Math.random() <= 0.3) { + // 成功,调用 onFuffiled,并传递女神的回复 + resolve(`${name} -> 邓哥:我是九,你是三,除了你还是你😘`); + } else { + // 失败,调用 onRejected,并传递女神的回复 + reject(`${name} -> 邓哥:你是个好人😜`); + } + }, 1000); + }); +} + +sendMessage('李建国') + .catch((reply) => { + // 失败,继续 + console.log(reply); + return sendMessage('王富贵'); + }) + .catch((reply) => { + // 失败,继续 + console.log(reply); + return sendMessage('周聚财'); + }) + .catch((reply) => { + // 失败,继续 + console.log(reply); + return sendMessage('刘人勇'); + }) + .then( + (reply) => { + // 成功,结束 + console.log(reply); + console.log('邓哥终于找到了自己的伴侣'); + }, + (reply) => { + // 最后一个也失败了 + console.log(reply); + console.log('邓哥命犯天煞孤星,无伴终老,孤独一生'); + } + ); diff --git a/02. Promise的链式调用/笔记.md b/02. Promise的链式调用/笔记.md new file mode 100644 index 0000000..f9341a8 --- /dev/null +++ b/02. Promise的链式调用/笔记.md @@ -0,0 +1,103 @@ +![image-20210618161125894](http://mdrs.yuanjin.tech/img/20210618161125.png) + +# catch方法 + +`.catch(onRejected)` = `.then(null, onRejected)` + +# 链式调用 + +![image-20210621103501094](http://mdrs.yuanjin.tech/img/20210621103501.png) + +1. then方法必定会返回一个新的Promise + + 可理解为`后续处理也是一个任务` + +2. 新任务的状态取决于后续处理: + + - 若没有相关的后续处理,新任务的状态和前任务一致,数据为前任务的数据 + + - 若有后续处理但还未执行,新任务挂起。 + - 若后续处理执行了,则根据后续处理的情况确定新任务的状态 + - 后续处理执行无错,新任务的状态为完成,数据为后续处理的返回值 + - 后续处理执行有错,新任务的状态为失败,数据为异常对象 + - 后续执行后返回的是一个任务对象,新任务的状态和数据与该任务对象一致 + +由于链式任务的存在,异步代码拥有了更强的表达力 + +```js +// 常见任务处理代码 + +/* + * 任务成功后,执行处理1,失败则执行处理2 + */ +pro.then(处理1).catch(处理2) + +/* + * 任务成功后,依次执行处理1、处理2 + */ +pro.then(处理1).then(处理2) + +/* + * 任务成功后,依次执行处理1、处理2,若任务失败或前面的处理有错,执行处理3 + */ +pro.then(处理1).then(处理2).catch(处理3) +``` + +# 邓哥的解决方案 + +```js +// 向某位女生发送一则表白短信 +// name: 女神的姓名 +// onFulffiled: 成功后的回调 +// onRejected: 失败后的回调 +function sendMessage(name) { + return new Promise((resolve, reject) => { + // 模拟 发送表白短信 + console.log( + `邓哥 -> ${name}:最近有谣言说我喜欢你,我要澄清一下,那不是谣言😘` + ); + console.log(`等待${name}回复......`); + // 模拟 女神回复需要一段时间 + setTimeout(() => { + // 模拟 有10%的几率成功 + if (Math.random() <= 0.1) { + // 成功,调用 onFuffiled,并传递女神的回复 + resolve(`${name} -> 邓哥:我是九,你是三,除了你还是你😘`); + } else { + // 失败,调用 onRejected,并传递女神的回复 + reject(`${name} -> 邓哥:你是个好人😜`); + } + }, 1000); + }); +} + +sendMessage('李建国') + .catch((reply) => { + // 失败,继续 + console.log(reply); + return sendMessage('王富贵'); + }) + .catch((reply) => { + // 失败,继续 + console.log(reply); + return sendMessage('周聚财'); + }) + .catch((reply) => { + // 失败,继续 + console.log(reply); + return sendMessage('刘人勇'); + }) + .then( + (reply) => { + // 成功,结束 + console.log(reply); + console.log('邓哥终于找到了自己的伴侣'); + }, + (reply) => { + // 最后一个也失败了 + console.log(reply); + console.log('邓哥命犯天煞孤星,无伴终老,孤独一生'); + } + ); +``` + diff --git a/02. Promise的链式调用/练习题.1/p1.js b/02. Promise的链式调用/练习题.1/p1.js new file mode 100644 index 0000000..8aa39b1 --- /dev/null +++ b/02. Promise的链式调用/练习题.1/p1.js @@ -0,0 +1,14 @@ +// 下面代码的输出结果是什么 + +new Promise((resolve, reject) => { + setTimeout(() => { + resolve(1); + }, 1000); +}) + .then((data) => { + throw 3; + return data + 1; + }) + .then((data) => { + console.log(data); + }); diff --git a/02. Promise的链式调用/练习题.1/p2.js b/02. Promise的链式调用/练习题.1/p2.js new file mode 100644 index 0000000..0aa574b --- /dev/null +++ b/02. Promise的链式调用/练习题.1/p2.js @@ -0,0 +1,19 @@ +// 下面代码的输出结果是什么 + +const pro = new Promise((resolve, reject) => { + resolve(1); +}) + .then((res) => { + console.log(res); + return 2; + }) + .catch((err) => { + return 3; + }) + .then((res) => { + console.log(res); + }); + +setTimeout(() => { + console.log(pro); +}, 1000); diff --git a/02. Promise的链式调用/练习题.1/p3.js b/02. Promise的链式调用/练习题.1/p3.js new file mode 100644 index 0000000..372ecda --- /dev/null +++ b/02. Promise的链式调用/练习题.1/p3.js @@ -0,0 +1,19 @@ +// 下面代码的输出结果是什么 + +const pro = new Promise((resolve, reject) => { + resolve(); +}) + .then((res) => { + console.log(res.toString()); // 报错 + return 2; + }) + .catch((err) => { + return 3; + }) + .then((res) => { + console.log(res); + }); + +setTimeout(() => { + console.log(pro); +}, 1000); diff --git a/02. Promise的链式调用/练习题.1/p4.js b/02. Promise的链式调用/练习题.1/p4.js new file mode 100644 index 0000000..27e61d1 --- /dev/null +++ b/02. Promise的链式调用/练习题.1/p4.js @@ -0,0 +1,16 @@ +// 下面代码的输出结果是什么 + +new Promise((resolve, reject) => { + resolve(1); +}) + .then((res) => { + console.log(res); + return new Error('2'); + }) + .catch((err) => { + throw err; + return 3; + }) + .then((res) => { + console.log(res); + }); diff --git a/02. Promise的链式调用/练习题.1/p5.js b/02. Promise的链式调用/练习题.1/p5.js new file mode 100644 index 0000000..7ebc06b --- /dev/null +++ b/02. Promise的链式调用/练习题.1/p5.js @@ -0,0 +1,18 @@ +// 下面的代码输出什么 + +const promise1 = new Promise((resolve, reject) => { + setTimeout(() => { + reject(); + }, 1000); +}); +const promise2 = promise1.catch(() => { + return 2; +}); + +console.log('promise1', promise1); +console.log('promise2', promise2); + +setTimeout(() => { + console.log('promise1', promise1); + console.log('promise2', promise2); +}, 2000); diff --git a/02. Promise的链式调用/练习题/p1.js b/02. Promise的链式调用/练习题/p1.js new file mode 100644 index 0000000..c77ad33 --- /dev/null +++ b/02. Promise的链式调用/练习题/p1.js @@ -0,0 +1,21 @@ +// 下面代码的输出结果是什么 +const pro1 = new Promise((resolve, reject) => { + setTimeout(() => { + resolve(1); + }, 1000); +}); + +const pro2 = pro1.then((data) => { + console.log(data); + return data + 1; +}); + +const pro3 = pro2.then((data) => { + console.log(data); +}); + +console.log(pro1, pro2, pro3); + +setTimeout(() => { + console.log(pro1, pro2, pro3); +}, 2000); diff --git a/02. Promise的链式调用/练习题/p2.js b/02. Promise的链式调用/练习题/p2.js new file mode 100644 index 0000000..824ce4d --- /dev/null +++ b/02. Promise的链式调用/练习题/p2.js @@ -0,0 +1,15 @@ +// 下面代码的输出结果是什么 + +new Promise((resolve, reject) => { + resolve(1); +}) + .then((res) => { + console.log(res); + return 2; + }) + .catch((err) => { + return 3; + }) + .then((res) => { + console.log(res); + }); diff --git a/02. Promise的链式调用/练习题/p3.js b/02. Promise的链式调用/练习题/p3.js new file mode 100644 index 0000000..b0f8dc7 --- /dev/null +++ b/02. Promise的链式调用/练习题/p3.js @@ -0,0 +1,15 @@ +// 下面代码的输出结果是什么 + +new Promise((resolve, reject) => { + resolve(); +}) + .then((res) => { + console.log(res.toString()); + return 2; + }) + .catch((err) => { + return 3; + }) + .then((res) => { + console.log(res); + }); diff --git a/02. Promise的链式调用/练习题/p4.js b/02. Promise的链式调用/练习题/p4.js new file mode 100644 index 0000000..47e70b0 --- /dev/null +++ b/02. Promise的链式调用/练习题/p4.js @@ -0,0 +1,16 @@ +// 下面代码的输出结果是什么 + +new Promise((resolve, reject) => { + throw new Error(1); +}) + .then((res) => { + console.log(res); + return new Error('2'); + }) + .catch((err) => { + throw err; + return 3; + }) + .then((res) => { + console.log(res); + }); diff --git a/02. Promise的链式调用/练习题/p5.js b/02. Promise的链式调用/练习题/p5.js new file mode 100644 index 0000000..7ebc06b --- /dev/null +++ b/02. Promise的链式调用/练习题/p5.js @@ -0,0 +1,18 @@ +// 下面的代码输出什么 + +const promise1 = new Promise((resolve, reject) => { + setTimeout(() => { + reject(); + }, 1000); +}); +const promise2 = promise1.catch(() => { + return 2; +}); + +console.log('promise1', promise1); +console.log('promise2', promise2); + +setTimeout(() => { + console.log('promise1', promise1); + console.log('promise2', promise2); +}, 2000); diff --git a/03. Promise的静态方法/1.js b/03. Promise的静态方法/1.js new file mode 100644 index 0000000..9cc1d4f --- /dev/null +++ b/03. Promise的静态方法/1.js @@ -0,0 +1,50 @@ +// 做饭 +function cook() { + return new Promise((resolve, reject) => { + console.log('邓哥打开了电饭煲'); + setTimeout(() => { + if (Math.random() < 0.5) { + resolve('饭已ok'); + } else { + reject('做饭却忘了加水,米饭变成了爆米花'); + } + }, 2000); + }); +} + +// 洗衣服 +function wash() { + return new Promise((resolve, reject) => { + console.log('邓哥打开了洗衣机'); + setTimeout(() => { + if (Math.random() < 0.5) { + resolve('衣服已经洗好'); + } else { + reject('洗衣服时停水了,洗了个寂寞'); + } + }, 2500); + }); +} + +// 打扫卫生 +function sweep() { + return new Promise((resolve, reject) => { + console.log('邓哥打开了扫地机器人'); + setTimeout(() => { + if (Math.random() < 0.5) { + resolve('地板扫的非常干净'); + } else { + reject('扫地机器人被哈士奇一爪掀翻了'); + } + }, 3000); + }); +} + +Promise.allSettled([cook(), wash(), sweep()]).then((result) => { + // 处理汇总结果 + + const report = result + .map((r) => (r.status === 'fulfilled' ? r.value : r.reason)) + .join(';'); + console.log(report); +}); diff --git a/03. Promise的静态方法/2.js b/03. Promise的静态方法/2.js new file mode 100644 index 0000000..e2512be --- /dev/null +++ b/03. Promise的静态方法/2.js @@ -0,0 +1,16 @@ +// const pro = new Promise((resolve, reject) => { +// reject(1); +// }); +// console.log(pro); + +const pro = Promise.race([ + Promise.reject(1), + Promise.reject(2), + Promise.resolve(3), +]); +setTimeout(() => { + console.log(pro); +}, 1000); +// pro.catch((result) => { +// console.log('失败', result.errors); +// }); diff --git a/03. Promise的静态方法/笔记.md b/03. Promise的静态方法/笔记.md new file mode 100644 index 0000000..5687989 --- /dev/null +++ b/03. Promise的静态方法/笔记.md @@ -0,0 +1,95 @@ +# 邓哥的新问题 + +邓嫂出门时,给邓哥交待了几个任务: + +1. 做饭 + + 可交给电饭煲完成 + +2. 洗衣服 + + 可交给洗衣机完成 + +3. 打扫卫生 + + 可交给扫地机器人完成 + +邓哥需要在所有任务结束后给邓嫂汇报工作,哪些成功了,哪些失败了 + +为了最大程度的节约时间,邓哥希望这些任务同时进行,最终汇总结果统一处理 + +image-20210621142519937 + +每个任务可以看做是一个返回Promise的函数 + +```js +// 做饭 +function cook() { + return new Promise((resolve, reject) => { + console.log('邓哥打开了电饭煲'); + setTimeout(() => { + if (Math.random() < 0.5) { + resolve('饭已ok'); + } else { + reject('做饭却忘了加水,米饭变成了爆米花'); + } + }, 2000); + }); +} + +// 洗衣服 +function wash() { + return new Promise((resolve, reject) => { + console.log('邓哥打开了洗衣机'); + setTimeout(() => { + if (Math.random() < 0.5) { + resolve('衣服已经洗好'); + } else { + reject('洗衣服时停水了,洗了个寂寞'); + } + }, 2500); + }); +} + +// 打扫卫生 +function sweep() { + return new Promise((resolve, reject) => { + console.log('邓哥打开了扫地机器人'); + setTimeout(() => { + if (Math.random() < 0.5) { + resolve('地板扫的非常干净'); + } else { + reject('扫地机器人被哈士奇一爪掀翻了'); + } + }, 3000); + }); +} + +``` + +如何利用这三个函数实现邓哥的要求呢? + +# Promise的静态方法 + +| 方法名 | 含义 | +| ---------------------------- | ------------------------------------------------------------ | +| Promise.resolve(data) | 直接返回一个完成状态的任务 | +| Promise.reject(reason) | 直接返回一个拒绝状态的任务 | +| Promise.all(任务数组) | 返回一个任务
任务数组全部成功则成功
任何一个失败则失败 | +| Promise.any(任务数组) | 返回一个任务
任务数组任一成功则成功
任务全部失败则失败 | +| Promise.allSettled(任务数组) | 返回一个任务
任务数组全部已决则成功
该任务不会失败 | +| Promise.race(任务数组) | 返回一个任务
任务数组任一已决则已决,状态和其一致 | +| | | + +# 邓哥的解决方案 + +```js +Promise.allSettled([cook(), wash(), sweep()]).then((result) => { + // 处理汇总结果 + const report = result + .map((r) => (r.status === 'fulfilled' ? r.value : r.reason)) + .join(';'); + console.log(report); +}); +``` + diff --git a/03. Promise的静态方法/练习题/p1.js b/03. Promise的静态方法/练习题/p1.js new file mode 100644 index 0000000..8ed6b1d --- /dev/null +++ b/03. Promise的静态方法/练习题/p1.js @@ -0,0 +1,30 @@ +/** + * 根据页码获取学生数据,返回Promise + * @param {Number} page 页码 + */ +function fetchStudents(page) { + return new Promise((resolve, reject) => { + setTimeout(() => { + if (Math.random() < 0.3) { + reject(new Error(`网络错误!获取第${page}页数据失败!`)); + return; + } + // 模拟学生数据 + const stus = new Array(10).fill(null).map((d, i) => ({ + id: `NO.${(page - 1) * 10 + i + 1}`, + name: `姓名${(page - 1) * 10 + i + 1}`, + })); + resolve(stus); + }, Math.floor(Math.random() * 5000)); + }); +} + +// 利用 fetchStudents 函数,完成下面的练习 + +// 获取1-10页的学生,最终按照页码的顺序合并成一个数组,任何一页的数据获取出现错误,则任务不再继续,打印错误消息 + +// 获取1-10页的学生,最终按照页码的顺序合并成一个数组,如果某些页码的数据获取失败,就不加入该数据即可 + +// 获取1-10页的学生,打印最先获取到的数据,如果全部都获取失败,则打印所有的错误消息 + +// 获取1-10页的学生,输出最先得到的结果(有结果输出结果,有错误输出错误) diff --git a/03. Promise的静态方法/练习题参考答案/p1.html b/03. Promise的静态方法/练习题参考答案/p1.html new file mode 100644 index 0000000..66fadbd --- /dev/null +++ b/03. Promise的静态方法/练习题参考答案/p1.html @@ -0,0 +1,12 @@ + + + + + + + Document + + + + + diff --git a/03. Promise的静态方法/练习题参考答案/p1.js b/03. Promise的静态方法/练习题参考答案/p1.js new file mode 100644 index 0000000..b2d4bf7 --- /dev/null +++ b/03. Promise的静态方法/练习题参考答案/p1.js @@ -0,0 +1,62 @@ +/** + * 根据页码获取学生数据,返回Promise + * @param {Number} page 页码 + */ +function fetchStudents(page) { + return new Promise((resolve, reject) => { + setTimeout(() => { + if (Math.random() < 0.3) { + reject(new Error(`网络错误!获取第${page}页数据失败!`)); + return; + } + // 模拟学生数据 + const stus = new Array(10).fill(null).map((d, i) => ({ + id: `NO.${(page - 1) * 10 + i + 1}`, + name: `姓名${(page - 1) * 10 + i + 1}`, + })); + resolve(stus); + }, Math.floor(Math.random() * 5000)); + }); +} + +// 利用 fetchStudents 函数,完成下面的练习 + +// 获取1-10页的学生,最终按照页码的顺序合并成一个数组,任何一页的数据获取出现错误,则任务不再继续,打印错误消息 + +const proms = new Array(10).fill(1).map((it, i) => fetchStudents(i + 1)); + +// Promise.all(proms) +// .then((result) => { +// console.log(result.flat()); +// }) +// .catch((err) => { +// console.log(err); +// }); + +// 获取1-10页的学生,最终按照页码的顺序合并成一个数组,如果某些页码的数据获取失败,就不加入该数据即可 +// Promise.allSettled(proms).then((result) => { +// result = result +// .filter((r) => r.status === 'fulfilled') +// .map((it) => it.value) +// .flat(); +// console.log(result); +// }); + +// 获取1-10页的学生,打印最先获取到的数据,如果全部都获取失败,则打印所有的错误消息 +// Promise.any(proms) +// .then((result) => { +// console.log(result); +// }) +// .catch((err) => { +// console.log(err.errors); +// }); + +// 获取1-10页的学生,输出最先得到的结果(有结果输出结果,有错误输出错误) +Promise.race(proms).then( + (result) => { + console.log(result); + }, + (err) => { + console.log(err); + } +); diff --git a/04. async和await/1.js b/04. async和await/1.js new file mode 100644 index 0000000..199761f --- /dev/null +++ b/04. async和await/1.js @@ -0,0 +1,29 @@ +function delay(duration) { + return new Promise((resolve, reject) => { + setTimeout(() => { + reject(); + }, duration); + }); +} + +// delay(1000).then(() => { +// console.log('执行某个东西'); +// }); + +(async () => { + try { + await delay(1000); + console.log('成功'); + } catch (err) { + console.log('失败'); + } +})(); + +// delay(1000).then( +// (data) => { +// console.log('成功'); +// }, +// (err) => { +// console.log('失败'); +// } +// ); diff --git a/04. async和await/2.js b/04. async和await/2.js new file mode 100644 index 0000000..854478a --- /dev/null +++ b/04. async和await/2.js @@ -0,0 +1,45 @@ +// 女神的名字数组 +const beautyGirls = ['梁平', '邱杰']; + +// 向某位女生发送一则表白短信 +// name: 女神的姓名 +function sendMessage(name) { + return new Promise((resolve, reject) => { + // 模拟 发送表白短信 + console.log( + `邓哥 -> ${name}:最近有谣言说我喜欢你,我要澄清一下,那不是谣言😘` + ); + console.log(`等待${name}回复......`); + // 模拟 女神回复需要一段时间 + setTimeout(() => { + // 模拟 有10%的几率成功 + if (Math.random() <= 0.1) { + // 成功,调用 onFuffiled,并传递女神的回复 + resolve(`${name} -> 邓哥:我是九,你是三,除了你还是你😘`); + } else { + // 失败,调用 onRejected,并传递女神的回复 + reject(`${name} -> 邓哥:你是个好人😜`); + } + }, 1000); + }); +} + +(async () => { + let isSuccess = false; + for (const name of beautyGirls) { + try { + const reply = await sendMessage(name); + console.log(reply); + console.log('表白成功!'); + isSuccess = true; + break; + } catch (reply) { + console.log(reply); + console.log('表白失败'); + } + } + + if (!isSuccess) { + console.log('邓哥注定孤独一生'); + } +})(); diff --git a/04. async和await/笔记.md b/04. async和await/笔记.md new file mode 100644 index 0000000..c68a530 --- /dev/null +++ b/04. async和await/笔记.md @@ -0,0 +1,177 @@ +![image-20210618161125894](http://mdrs.yuanjin.tech/img/20210618161125.png) + +# 消除回调 + +有了Promise,异步任务就有了一种统一的处理方式 + +有了统一的处理方式,ES官方就可以对其进一步优化 + +ES7推出了两个关键字`async`和`await`,用于更加优雅的表达Promise + +## async + +async关键字用于修饰函数,被它修饰的函数,一定返回Promise + +```js +async function method1(){ + return 1; // 该函数的返回值是Promise完成后的数据 +} + +method1(); // Promise { 1 } + +async function method2(){ + return Promise.resolve(1); // 若返回的是Promise,则method得到的Promise状态和其一致 +} + +method2(); // Promise { 1 } + +async function method3(){ + throw new Error(1); // 若执行过程报错,则任务是rejected +} + +method3(); // Promise { Error(1) } +``` + +## await + +`await`关键字表示等待某个Promise完成,**它必须用于`async`函数中** + +```js +async function method(){ + const n = await Promise.resolve(1); + console.log(n); // 1 +} + +// 上面的函数等同于 +function method(){ + return new Promise((resolve, reject)=>{ + Promise.resolve(1).then(n=>{ + console.log(n); + resolve(1) + }) + }) +} +``` + +`await`也可以等待其他数据 + +```js +async function method(){ + const n = await 1; // 等同于 await Promise.resolve(1) +} +``` + +如果需要针对失败的任务进行处理,可以使用`try-catch`语法 + +```js +async function method(){ + try{ + const n = await Promise.reject(123); // 这句代码将抛出异常 + console.log('成功', n) + } + catch(err){ + console.log('失败', err) + } +} + +method(); // 输出: 失败 123 +``` + + + +# 邓哥表白的完美解决方案 + +邓哥的女神可不是只有4位,而是40位! + +为了更加方便的编写表白代码,邓哥决定把这40位女神放到一个数组中,然后利用async和await轻松完成代码 + +```js +// 女神的名字数组 +const beautyGirls = [ + '梁平', + '邱杰', + '王超', + '冯秀兰', + '赖军', + '顾强', + '戴敏', + '吕涛', + '冯静', + '蔡明', + '廖磊', + '冯洋', + '韩杰', + '江涛', + '文艳', + '杜秀英', + '丁艳', + '邓静', + '江刚', + '乔刚', + '史平', + '康娜', + '袁磊', + '龙秀英', + '姚静', + '潘娜', + '萧磊', + '邵勇', + '李芳', + '谭芳', + '夏秀英', + '程娜', + '武杰', + '崔军', + '廖勇', + '崔强', + '康秀英', + '余磊', + '邵勇', + '贺涛', +]; + +// 向某位女生发送一则表白短信 +// name: 女神的姓名 +function sendMessage(name) { + return new Promise((resolve, reject) => { + // 模拟 发送表白短信 + console.log( + `邓哥 -> ${name}:最近有谣言说我喜欢你,我要澄清一下,那不是谣言😘` + ); + console.log(`等待${name}回复......`); + // 模拟 女神回复需要一段时间 + setTimeout(() => { + // 模拟 有10%的几率成功 + if (Math.random() <= 0.1) { + // 成功,调用 onFuffiled,并传递女神的回复 + resolve(`${name} -> 邓哥:我是九,你是三,除了你还是你😘`); + } else { + // 失败,调用 onRejected,并传递女神的回复 + reject(`${name} -> 邓哥:你是个好人😜`); + } + }, 1000); + }); +} + +// 批量表白的程序 +async function proposal() { + let isSuccess = false; + for (const girl of beautyGirls) { + try { + const reply = await sendMessage(girl); + console.log(reply); + console.log('表白成功!'); + isSuccess = true; + break; + } catch (reply) { + console.log(reply); + console.log('表白失败'); + } + } + if (!isSuccess) { + console.log('邓哥注定孤独一生'); + } +} +proposal(); +``` + diff --git a/04. async和await/练习题/p1.html b/04. async和await/练习题/p1.html new file mode 100644 index 0000000..d169e85 --- /dev/null +++ b/04. async和await/练习题/p1.html @@ -0,0 +1,13 @@ + + + diff --git a/04. async和await/练习题/p2.js b/04. async和await/练习题/p2.js new file mode 100644 index 0000000..8950bb2 --- /dev/null +++ b/04. async和await/练习题/p2.js @@ -0,0 +1,7 @@ +// 完成delay函数 +// 该函数可以等待一段指定的时间 +// 返回Promise +function delay(duration) {} + +// 利用delay函数,等待3次,每次等待1秒,每次等待完成后输出ok +// 等待1秒->ok->等待1秒->ok->等待1秒->ok diff --git a/04. async和await/练习题参考答案/p1.html b/04. async和await/练习题参考答案/p1.html new file mode 100644 index 0000000..1d4e7ff --- /dev/null +++ b/04. async和await/练习题参考答案/p1.html @@ -0,0 +1,19 @@ + + + diff --git a/04. async和await/练习题参考答案/p2.js b/04. async和await/练习题参考答案/p2.js new file mode 100644 index 0000000..f2f54d0 --- /dev/null +++ b/04. async和await/练习题参考答案/p2.js @@ -0,0 +1,33 @@ +// 完成delay函数 +// 该函数可以等待一段指定的时间 +// 返回Promise +function delay(duration) { + return new Promise((resolve, reject) => { + setTimeout(() => { + resolve(); + }, duration); + }); +} + +// 利用delay函数,等待3次,每次等待1秒,每次等待完成后输出ok +// 等待1秒->ok->等待1秒->ok->等待1秒->ok + +(async () => { + for (let i = 0; i < 3; i++) { + await delay(1000); + console.log('ok'); + } +})(); + +// delay(1000) +// .then(() => { +// console.log('ok'); +// return delay(1000); +// }) +// .then(() => { +// console.log('ok'); +// return delay(1000); +// }) +// .then(() => { +// console.log('ok'); +// }); diff --git a/05. Promise相关面试题/1.js b/05. Promise相关面试题/1.js new file mode 100644 index 0000000..df5a995 --- /dev/null +++ b/05. Promise相关面试题/1.js @@ -0,0 +1,11 @@ +const promise = new Promise((resolve, reject) => { + console.log(1); + resolve(); + console.log(2); +}); + +promise.then(() => { + console.log(3); +}); + +console.log(4); diff --git a/05. Promise相关面试题/2.js b/05. Promise相关面试题/2.js new file mode 100644 index 0000000..2af03b7 --- /dev/null +++ b/05. Promise相关面试题/2.js @@ -0,0 +1,14 @@ +setTimeout(() => { + console.log(1); +}); + +const promise = new Promise((resolve, reject) => { + console.log(2); + resolve(); +}); + +promise.then(() => { + console.log(3); +}); + +console.log(4); diff --git a/05. Promise相关面试题/3.js b/05. Promise相关面试题/3.js new file mode 100644 index 0000000..b572e96 --- /dev/null +++ b/05. Promise相关面试题/3.js @@ -0,0 +1,16 @@ +const promise1 = new Promise((resolve, reject) => { + setTimeout(() => { + reject(); + }, 1000); +}); +const promise2 = promise1.catch(() => { + return 2; +}); + +console.log('promise1', promise1); +console.log('promise2', promise2); + +setTimeout(() => { + console.log('promise1', promise1); + console.log('promise2', promise2); +}, 2000); diff --git a/05. Promise相关面试题/4.js b/05. Promise相关面试题/4.js new file mode 100644 index 0000000..453f854 --- /dev/null +++ b/05. Promise相关面试题/4.js @@ -0,0 +1,14 @@ +async function m() { + console.log(0); + const n = await 1; + console.log(n); +} + +// function m() { +// return Promise.resolve(1).then((n) => { +// console.log(n); +// }); +// } + +m(); +console.log(2); diff --git a/05. Promise相关面试题/5.js b/05. Promise相关面试题/5.js new file mode 100644 index 0000000..88bd44b --- /dev/null +++ b/05. Promise相关面试题/5.js @@ -0,0 +1,12 @@ +async function m() { + console.log(0); + const n = await 1; + console.log(n); +} + +(async () => { + await m(); + console.log(2); +})(); + +console.log(3); diff --git a/05. Promise相关面试题/6.js b/05. Promise相关面试题/6.js new file mode 100644 index 0000000..1c4f9f2 --- /dev/null +++ b/05. Promise相关面试题/6.js @@ -0,0 +1,23 @@ +async function m1() { + return 1; +} + +async function m2() { + const n = await m1(); + console.log(n); + return 2; +} + +async function m3() { + const n = m2(); + console.log(n); + return 3; +} + +m3().then((n) => { + console.log(n); +}); + +m3(); + +console.log(4); diff --git a/05. Promise相关面试题/7.js b/05. Promise相关面试题/7.js new file mode 100644 index 0000000..507f0ed --- /dev/null +++ b/05. Promise相关面试题/7.js @@ -0,0 +1 @@ +Promise.resolve(1).then(2).then(Promise.resolve(3)).then(console.log); diff --git a/05. Promise相关面试题/8.js b/05. Promise相关面试题/8.js new file mode 100644 index 0000000..09cff15 --- /dev/null +++ b/05. Promise相关面试题/8.js @@ -0,0 +1,28 @@ +var a; +var b = new Promise((resolve, reject) => { + console.log('promise1'); + setTimeout(() => { + resolve(); + }, 1000); +}) + .then(() => { + console.log('promise2'); + }) + .then(() => { + console.log('promise3'); + }) + .then(() => { + console.log('promise4'); + }); + +a = new Promise(async (resolve, reject) => { + console.log(a); + await b; + console.log(a); + console.log('after1'); + await a; + resolve(true); + console.log('after2'); +}); + +console.log('end'); diff --git a/05. Promise相关面试题/9.js b/05. Promise相关面试题/9.js new file mode 100644 index 0000000..0fac89d --- /dev/null +++ b/05. Promise相关面试题/9.js @@ -0,0 +1,24 @@ +async function async1() { + console.log('async1 start'); + await async2(); + console.log('async1 end'); +} +async function async2() { + console.log('async2'); +} + +console.log('script start'); + +setTimeout(function () { + console.log('setTimeout'); +}, 0); + +async1(); + +new Promise(function (resolve) { + console.log('promise1'); + resolve(); +}).then(function () { + console.log('promise2'); +}); +console.log('script end'); diff --git a/05. Promise相关面试题/笔记.md b/05. Promise相关面试题/笔记.md new file mode 100644 index 0000000..dafb9cf --- /dev/null +++ b/05. Promise相关面试题/笔记.md @@ -0,0 +1,311 @@ +# 面试题考点 + +## Promise的基本概念 + +![image-20210618161125894](http://mdrs.yuanjin.tech/img/20210618161125.png) + +## 链式调用规则 + +![image-20210621103501094](http://mdrs.yuanjin.tech/img/20210621103501.png) + +![image-20210621103501094](http://mdrs.yuanjin.tech/img/20210621103501.png) + +1. then方法必定会返回一个新的Promise + + 可理解为`后续处理也是一个任务` + +2. 新任务的状态取决于后续处理: + + - 若没有相关的后续处理,新任务的状态和前任务一致,数据为前任务的数据 + + - 若有后续处理但还未执行,新任务挂起。 + - 若后续处理执行了,则根据后续处理的情况确定新任务的状态 + - 后续处理执行无错,新任务的状态为完成,数据为后续处理的返回值 + - 后续处理执行有错,新任务的状态为失败,数据为异常对象 + - 后续执行后返回的是一个任务对象,新任务的状态和数据与该任务对象一致 + +## Promise的静态方法 + +| 方法名 | 含义 | +| ---------------------------- | ------------------------------------------------------------ | +| Promise.resolve(data) | 直接返回一个完成状态的任务 | +| Promise.reject(reason) | 直接返回一个拒绝状态的任务 | +| Promise.all(任务数组) | 返回一个任务
任务数组全部成功则成功
任何一个失败则失败 | +| Promise.any(任务数组) | 返回一个任务
任务数组任一成功则成功
任务全部失败则失败 | +| Promise.allSettled(任务数组) | 返回一个任务
任务数组全部已决则成功
该任务不会失败 | +| Promise.race(任务数组) | 返回一个任务
任务数组任一已决则已决,状态和其一致 | +| | | + +## async和await + +有了Promise,异步任务就有了一种统一的处理方式 + +有了统一的处理方式,ES官方就可以对其进一步优化 + +ES7推出了两个关键字`async`和`await`,用于更加优雅的表达Promise + +### async + +async关键字用于修饰函数,被它修饰的函数,一定返回Promise + +```js +async function method1(){ + return 1; // 该函数的返回值是Promise完成后的数据 +} + +method1(); // Promise { 1 } + +async function method2(){ + return Promise.resolve(1); // 若返回的是Promise,则method得到的Promise状态和其一致 +} + +method2(); // Promise { 1 } + +async function method3(){ + throw new Error(1); // 若执行过程报错,则任务是rejected +} + +method3(); // Promise { Error(1) } +``` + +### await + +`await`关键字表示等待某个Promise完成,**它必须用于`async`函数中** + +```js +async function method(){ + const n = await Promise.resolve(1); + console.log(n); // 1 +} + +// 上面的函数等同于 +function method(){ + return new Promise((resolve, reject)=>{ + Promise.resolve(1).then(n=>{ + console.log(n); + resolve(1) + }) + }) +} +``` + +`await`也可以等待其他数据 + +```js +async function method(){ + const n = await 1; // 等同于 await Promise.resolve(1) +} +``` + +如果需要针对失败的任务进行处理,可以使用`try-catch`语法 + +```js +async function method(){ + try{ + const n = await Promise.reject(123); // 这句代码将抛出异常 + console.log('成功', n) + } + catch(err){ + console.log('失败', err) + } +} + +method(); // 输出: 失败 123 +``` + +## 事件循环 + +根据目前所学,进入事件队列的函数有以下几种: + +- `setTimeout`的回调,宏任务(macro task) +- `setInterval`的回调,宏任务(macro task) +- Promise的`then`函数回调,**微任务**(micro task) +- `requestAnimationFrame`的回调,宏任务(macro task) +- 事件处理函数,宏任务(macro task) + +# 面试题 + +1. 下面代码的输出结果是什么 + + ```js + const promise = new Promise((resolve, reject) => { + console.log(1); + resolve(); + console.log(2); + }) + + promise.then(() => { + console.log(3); + }) + + console.log(4); + ``` + +2. 下面代码的输出结果是什么 + + ```js + const promise = new Promise((resolve, reject) => { + console.log(1); + setTimeout(()=>{ + console.log(2) + resolve(); + console.log(3); + }) + }) + + promise.then(() => { + console.log(4); + }) + + console.log(5); + ``` + +3. 下面代码的输出结果是什么 + + ```js + const promise1 = new Promise((resolve, reject) => { + setTimeout(() => { + resolve() + }, 1000) + }) + const promise2 = promise1.catch(() => { + return 2; + }) + + console.log('promise1', promise1) + console.log('promise2', promise2) + + setTimeout(() => { + console.log('promise1', promise1) + console.log('promise2', promise2) + }, 2000) + ``` + +4. 下面代码的输出结果是什么 + + ```js + async function m(){ + const n = await 1; + console.log(n); + } + + m(); + console.log(2); + ``` + +5. 下面代码的输出结果是什么 + + ```js + async function m(){ + const n = await 1; + console.log(n); + } + + (async ()=>{ + await m(); + console.log(2); + })(); + + console.log(3); + ``` + + + +6. 下面代码的输出结果是什么 + + ```js + async function m1(){ + return 1; + } + + async function m2(){ + const n = await m1(); + console.log(n) + return 2; + } + + async function m3(){ + const n = m2(); + console.log(n); + return 3; + } + + m3().then(n=>{ + console.log(n); + }); + + m3(); + + console.log(4); + ``` + +7. 下面代码的输出结果是什么 + + ```js + Promise.resolve(1) + .then(2) + .then(Promise.resolve(3)) + .then(console.log) + ``` + +8. 下面代码的输出结果是什么 + + ```js + var a; + var b = new Promise((resolve, reject) => { + console.log('promise1'); + setTimeout(()=>{ + resolve(); + }, 1000); + }).then(() => { + console.log('promise2'); + }).then(() => { + console.log('promise3'); + }).then(() => { + console.log('promise4'); + }); + + a = new Promise(async (resolve, reject) => { + console.log(a); + await b; + console.log(a); + console.log('after1'); + await a + resolve(true); + console.log('after2'); + }); + + console.log('end'); + ``` + +9. 下面代码的输出结果是什么 + + ```js + async function async1() { + console.log('async1 start'); + await async2(); + console.log('async1 end'); + } + async function async2() { + console.log('async2'); + } + + console.log('script start'); + + setTimeout(function() { + console.log('setTimeout'); + }, 0) + + async1(); + + new Promise(function(resolve) { + console.log('promise1'); + resolve(); + }).then(function() { + console.log('promise2'); + }); + console.log('script end'); + ``` + + + diff --git a/06. 手写Promise/MyPromise.js b/06. 手写Promise/MyPromise.js new file mode 100644 index 0000000..5c5ae5b --- /dev/null +++ b/06. 手写Promise/MyPromise.js @@ -0,0 +1,287 @@ +// 记录Promise的三种状态 +const PENDING = 'pending'; +const FULFILLED = 'fulfilled'; +const REJECTED = 'rejected'; + +/** + * 运行一个微队列任务 + * 把传递的函数放到微队列中 + * @param {Function} callback + */ +function runMicroTask(callback) { + // 判断node环境 + // 为了避免「变量未定义」的错误,这里最好加上前缀globalThis + // globalThis是一个关键字,指代全局对象,浏览器环境为window,node环境为global + if (globalThis.process && globalThis.process.nextTick) { + process.nextTick(callback); + } else if (globalThis.MutationObserver) { + const p = document.createElement('p'); + const observer = new MutationObserver(callback); + observer.observe(p, { + childList: true, // 观察该元素内部的变化 + }); + p.innerHTML = '1'; + } else { + setTimeout(callback, 0); + } +} + +/** + * 判断一个数据是否是Promise对象 + * @param {any} obj + * @returns + */ +function isPromise(obj) { + return !!(obj && typeof obj === 'object' && typeof obj.then === 'function'); +} + +class MyPromise { + /** + * 创建一个Promise + * @param {Function} executor 任务执行器,立即执行 + */ + constructor(executor) { + this._state = PENDING; // 状态 + this._value = undefined; // 数据 + this._handlers = []; // 处理函数形成的队列 + try { + executor(this._resolve.bind(this), this._reject.bind(this)); + } catch (error) { + this._reject(error); + console.error(error); + } + } + + /** + * 向处理队列中添加一个函数 + * @param {Function} executor 添加的函数 + * @param {String} state 该函数什么状态下执行 + * @param {Function} resolve 让then函数返回的Promise成功 + * @param {Function} reject 让then函数返回的Promise失败 + */ + _pushHandler(executor, state, resolve, reject) { + this._handlers.push({ + executor, + state, + resolve, + reject, + }); + } + + /** + * 根据实际情况,执行队列 + */ + _runHandlers() { + if (this._state === PENDING) { + // 目前任务仍在挂起 + return; + } + while (this._handlers[0]) { + const handler = this._handlers[0]; + this._runOneHandler(handler); + this._handlers.shift(); + } + } + + /** + * 处理一个handler + * @param {Object} handler + */ + _runOneHandler({ executor, state, resolve, reject }) { + runMicroTask(() => { + if (this._state !== state) { + // 状态不一致,不处理 + return; + } + + if (typeof executor !== 'function') { + // 传递后续处理并非一个函数 + this._state === FULFILLED ? resolve(this._value) : reject(this._value); + return; + } + try { + const result = executor(this._value); + if (isPromise(result)) { + result.then(resolve, reject); + } else { + resolve(result); + } + } catch (error) { + reject(error); + console.error(error); + } + }); + } + + /** + * Promise A+规范的then + * @param {Function} onFulfilled + * @param {Function} onRejected + */ + then(onFulfilled, onRejected) { + return new MyPromise((resolve, reject) => { + this._pushHandler(onFulfilled, FULFILLED, resolve, reject); + this._pushHandler(onRejected, REJECTED, resolve, reject); + this._runHandlers(); // 执行队列 + }); + } + + /** + * 仅处理失败的场景 + * @param {Function} onRejected + */ + catch(onRejected) { + return this.then(null, onRejected); + } + + /** + * 无论成功还是失败都会执行回调 + * @param {Function} onSettled + */ + finally(onSettled) { + return this.then( + (data) => { + onSettled(); + return data; + }, + (reason) => { + onSettled(); + throw reason; + } + ); + } + + /** + * 更改任务状态 + * @param {String} newState 新状态 + * @param {any} value 相关数据 + */ + _changeState(newState, value) { + if (this._state !== PENDING) { + // 目前状态已经更改 + return; + } + this._state = newState; + this._value = value; + this._runHandlers(); // 状态变化,执行队列 + } + + /** + * 标记当前任务完成 + * @param {any} data 任务完成的相关数据 + */ + _resolve(data) { + this._changeState(FULFILLED, data); + } + + /** + * 标记当前任务失败 + * @param {any} reason 任务失败的相关数据 + */ + _reject(reason) { + this._changeState(REJECTED, reason); + } + + /** + * 返回一个已完成的Promise + * 特殊情况: + * 1. 传递的data本身就是ES6的Promise对象 + * 2. 传递的data是PromiseLike(Promise A+),返回新的Promise,状态和其保持一致即可 + * @param {any} data + */ + static resolve(data) { + if (data instanceof MyPromise) { + return data; + } + return new MyPromise((resolve, reject) => { + if (isPromise(data)) { + data.then(resolve, reject); + } else { + resolve(data); + } + }); + } + + /** + * 得到一个被拒绝的Promise + * @param {any}} reason + */ + static reject(reason) { + return new MyPromise((resolve, reject) => { + reject(reason); + }); + } + + /** + * 得到一个新的Promise + * 该Promise的状态取决于proms的执行 + * proms是一个迭代器,包含多个Promise + * 全部Promise成功,则返回的Promise成功,数据为所有Promise成功的数据,并且顺序是按照传入的顺序排列 + * 只要有一个Promise失败,则返回的Promise失败,原因是第一个失败的Promise的原因 + * @param {iterator} proms + */ + static all(proms) { + return new MyPromise((resolve, reject) => { + try { + const results = []; + let count = 0; // Promise的总数 + let fulfilledCount = 0; // 已完成的数量 + for (const p of proms) { + let i = count; + count++; + MyPromise.resolve(p).then((data) => { + fulfilledCount++; + results[i] = data; + if (fulfilledCount === count) { + // 当前是最后一个Promise完成了 + resolve(results); + } + }, reject); + } + if (count === 0) { + resolve(results); + } + } catch (error) { + reject(error); + console.error(error); + } + }); + } + + /** + * 等待所有的Promise有结果之后 + * 该方法返回的Promise完成 + * 并且按照顺序将所有结果汇总 + * @param {iterator} proms + */ + static allSettled(proms) { + const ps = []; + for (const p of proms) { + ps.push( + MyPromise.resolve(p).then( + (value) => ({ + status: FULFILLED, + value, + }), + (reason) => ({ + status: REJECTED, + reason, + }) + ) + ); + } + return MyPromise.all(ps); + } + + /** + * 返回的Promise与第一个有结果的一致 + * @param {iterator} proms + */ + static race(proms) { + return new MyPromise((resolve, reject) => { + for (const p of proms) { + MyPromise.resolve(p).then(resolve, reject); + } + }); + } +} diff --git a/06. 手写Promise/说明.md b/06. 手写Promise/说明.md new file mode 100644 index 0000000..c91a676 --- /dev/null +++ b/06. 手写Promise/说明.md @@ -0,0 +1,3 @@ +手写 Promise 课程资料,仅提供最终的完整代码 + +至于每节课的代码,请同学们理解视频课程内容,然后自行手写,以达到最好的学习效果 diff --git a/知识图谱.drawio b/知识图谱.drawio new file mode 100644 index 0000000..2400597 --- /dev/null +++ b/知识图谱.drawio @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file