一、Promise 的基本使用

1.1、Promise 简介

Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。

所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。

Promise对象有以下两个特点。

(1)对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。

(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。

1.2、Promise 的基本使用

新建的Promise就是一个容器,里面可以存放异步操作,也可以存放同步操作

  1. const fs = require("fs");
  2. const path = require("path");
  3. let filePath = path.join(__dirname, "files", "3.txt");
  4. // 异步操作可能成功或者失败
  5. // 第一个形参resolve , 成功的时候执行的函数
  6. // 第二个形参reject , 失败的时候执行的函数
  7. let p1 = new Promise((resolve, reject)=>{
  8. //1、同步代码
  9. console.log("同步代码");
  10. // pending(进行中)、fulfilled(已成功)和rejected(已失败)
  11. //2、异步代码
  12. fs.readFile(filePath,"utf-8",(error1, data1)=>{
  13. if(error1){
  14. //失败的时候做的事情
  15. reject(error1); // 调用后面p1.then的第二个函数
  16. }
  17. //读取完之后做的事情
  18. resolve(data1); // 调用后面p1.then的第二个函数
  19. })
  20. });
  21. p1.then((data1)=>{
  22. console.log("读取成功", data1);
  23. },(error1)=>{
  24. console.log("读取失败", error1);
  25. });

1.3、Promise的then链式调用的特点

链式调用的特点:
1、第一个then执行完会执行第二个then
2、then里面的函数的返回值,会被下一个then的形参接收
3、如果返回的是一个promise对象,下一个then的形参接收到的不是这个promise对象,而是这个promise对象内部调用resolve时候的实际参数

  1. const fs = require("fs");
  2. const path = require("path");
  3. let filePath = path.join(__dirname, "files", "3.txt");
  4. // 新建的Promise就是一个容器,里面可以存放异步操作,也可以存放同步操作
  5. // 异步操作可能成功或者失败
  6. // 第一个形参resolve , 成功的时候执行的函数
  7. // 第二个形参reject , 失败的时候执行的函数
  8. let p1 = new Promise((resolve, reject)=>{
  9. //1、同步代码
  10. // console.log("同步代码");
  11. // pending(进行中)、fulfilled(已成功)和rejected(已失败)
  12. //2、异步代码
  13. fs.readFile(filePath,"utf-8",(error1, data1)=>{
  14. if(error1){
  15. //失败的时候做的事情
  16. reject(error1)
  17. }
  18. //读取完之后做的事情
  19. resolve("resolve的形参")
  20. })
  21. });
  22. //业内把promise then的写法被称为开火车开发
  23. p1.then((data1)=>{
  24. return p1
  25. },(error1)=>{
  26. console.log("读取失败", error1);
  27. return error1
  28. }).then((data)=>{
  29. console.log(data); // “resolve("resolve的形参")
  30. });

二、使用promise实现之前文件读取的案例

2.1、基础版

  1. //基础版
  2. const fs = require("fs");
  3. const path = require("path");
  4. let filePath1 = path.join(__dirname, "files", "1.txt"); // 我
  5. let filePath2 = path.join(__dirname, "files", "2.txt"); // 爱
  6. let filePath3 = path.join(__dirname, "files", "3.txt"); // Node.js
  7. let p1 = new Promise((resolve, reject)=>{
  8. //1、同步代码
  9. // console.log("同步代码");
  10. // pending(进行中)、fulfilled(已成功)和rejected(已失败)
  11. //2、异步代码
  12. fs.readFile(filePath1,"utf-8",(error1, data1)=>{
  13. if(error1){
  14. //失败的时候做的事情
  15. reject(error1);
  16. }
  17. //读取完之后做的事情
  18. resolve(data1)
  19. })
  20. });
  21. let p2 = new Promise((resolve, reject)=>{
  22. //1、同步代码
  23. // console.log("同步代码");
  24. // pending(进行中)、fulfilled(已成功)和rejected(已失败)
  25. //2、异步代码
  26. fs.readFile(filePath2,"utf-8",(error1, data1)=>{
  27. if(error1){
  28. //失败的时候做的事情
  29. reject(error1);
  30. }
  31. //读取完之后做的事情
  32. resolve(data1)
  33. })
  34. });
  35. let p3 = new Promise((resolve, reject)=>{
  36. //1、同步代码
  37. // console.log("同步代码");
  38. // pending(进行中)、fulfilled(已成功)和rejected(已失败)
  39. //2、异步代码
  40. fs.readFile(filePath3,"utf-8",(error1, data1)=>{
  41. if(error1){
  42. //失败的时候做的事情
  43. reject(error1)
  44. }
  45. //读取完之后做的事情
  46. resolve(data1)
  47. })
  48. });
  49. let str1 = "";
  50. p1.then((data)=>{
  51. str1+=data;
  52. return p2
  53. },(error1)=>{
  54. console.log("读取文件1失败", error1);
  55. return error1
  56. }).then((data)=>{
  57. str1+=data;
  58. return p3;
  59. }).then((data)=>{
  60. str1+=data;
  61. console.log(str1);
  62. });

2.2、封装函数版

  1. const fs = require("fs");
  2. const path = require("path");
  3. let filePath1 = path.join(__dirname, "files", "1.txt");
  4. let filePath2 = path.join(__dirname, "files", "2.txt");
  5. let filePath3 = path.join(__dirname, "files", "3.txt");
  6. function readFilePromise(filePath){
  7. return new Promise((resolve, reject)=>{
  8. fs.readFile(filePath,"utf-8",(error1, data1)=>{
  9. if(error1){
  10. //失败的时候做的事情
  11. reject(error1);
  12. }
  13. //读取完之后做的事情
  14. resolve(data1)
  15. })
  16. });
  17. }
  18. let str1 = "";
  19. readFilePromise(filePath1).then((data)=>{
  20. str1+=data;
  21. return readFilePromise(filePath2)
  22. },(error1)=>{
  23. console.log("读取文件1失败", error1);
  24. return error1
  25. }).then((data)=>{
  26. str1+=data;
  27. return readFilePromise(filePath3);
  28. }).then((data)=>{
  29. str1+=data;
  30. console.log(str1);
  31. });

2.3、util版本

node中有一个util工具模块下,有一个promisify方法,这个方法相当于封装了一个返回promise对象的函数。

文档网址:http://nodejs.cn/api/util.html#util_util_promisify_original

传入一个遵循常见的错误优先的回调风格的函数(即以 (err, value) => ... 回调作为最后一个参数),并返回一个返回 promise 的版本。

  1. let readFilePromise = util.promisify(fs.readFile); //这一句代码相当于下面的整个函数的代码
  2. // function readFilePromise(filePath){
  3. // return new Promise((resolve, reject)=>{
  4. //
  5. // fs.readFile(filePath,"utf-8",(error1, data1)=>{
  6. // if(error1){
  7. // //失败的时候做的事情
  8. // reject(error1);
  9. // }
  10. // //读取完之后做的事情
  11. // resolve(data1)
  12. // })
  13. // });
  14. // }
  15. // 总结 :util.promisify(fs.readFile) 得到一个promise对象

完整代码:

  1. const fs = require("fs");
  2. const path = require("path");
  3. const util = require("util");
  4. let filePath1 = path.join(__dirname, "files", "1.txt");
  5. let filePath2 = path.join(__dirname, "files", "2.txt");
  6. let filePath3 = path.join(__dirname, "files", "3.txt");
  7. let filePath4 = path.join(__dirname, "files", "data.txt");
  8. let readFilePromise = util.promisify(fs.readFile);
  9. let writeFilePromise = util.promisify(fs.writeFile);
  10. let str1 = "";
  11. readFilePromise(filePath1).then((data)=>{
  12. str1+=data;
  13. return readFilePromise(filePath2)
  14. },(error1)=>{
  15. console.log("读取文件1失败", error1);
  16. return error1
  17. }).then((data)=>{
  18. str1+=data;
  19. return readFilePromise(filePath3);
  20. }).then((data)=>{
  21. str1+=data;
  22. console.log(str1);
  23. writeFilePromise(filePath4, str1);
  24. });

2.4、all版本

  1. const fs = require("fs");
  2. const path = require("path");
  3. const util = require("util");
  4. let filePath1 = path.join(__dirname, "files", "1.txt");
  5. let filePath2 = path.join(__dirname, "files", "2.txt");
  6. let filePath3 = path.join(__dirname, "files", "3.txt");
  7. let filePath4 = path.join(__dirname, "files", "data.txt");
  8. let readFilePromise = util.promisify(fs.readFile); //这一句代码相当于下面的整个函数的代码
  9. let writeFilePromise = util.promisify(fs.writeFile);
  10. Promise.all([readFilePromise(filePath1,"utf-8"), readFilePromise(filePath2,"utf-8"),readFilePromise(filePath3,"utf-8")]).then((data)=>{
  11. let str1 = data.join("");
  12. writeFilePromise(filePath4,str1);
  13. }).catch((error)=>{
  14. //只要执行p1,p2时其中一个报错,就会执行这里的代码
  15. console.log(error);
  16. });

2.5、async+await版本

  1. const fs = require("fs");
  2. const path = require("path");
  3. const util = require("util");
  4. let filePath1 = path.join(__dirname, "files", "1.txt");
  5. let filePath2 = path.join(__dirname, "files", "2.txt");
  6. let filePath3 = path.join(__dirname, "files", "3.txt");
  7. let filePath4 = path.join(__dirname, "files", "data.txt");
  8. let readFile = util.promisify(fs.readFile);
  9. let writeFile = util.promisify(fs.writeFile);
  10. async function func() {
  11. let data1 = await readFile(filePath1, "utf-8");
  12. let data2 = await readFile(filePath2, "utf-8");
  13. let data3 = await readFile(filePath3, "utf-8");
  14. console.log(data1+data2+data3);
  15. writeFile(filePath4, data1+data2+data3)
  16. }
  17. func();

三、Promise的常用其他方法

3.1、promise对象catch()方法和finally()方法

  1. //一般,我们会把以下代码:
  2. p1.then((data1)=>{
  3. console.log("承诺成功", data1);
  4. },(error1)=>{
  5. console.log("承诺失败", error1);
  6. });
  7. //写成
  8. p1.then((data1)=>{
  9. console.log("承诺成功", data1);
  10. }).catch((error1)=>{
  11. console.log("承诺失败", error1);
  12. }).finally(()=>{
  13. console.log("承诺成功与失败都会执行这里的代码");
  14. });

3.2、Promise类的all() 方法

参数:是一个数组,数组元素是Promise实例对象,只有数组里面所有的Promise成功了,则才会执行 then 第一个回调

  1. const fs = require("fs");
  2. const path = require("path");
  3. let filePath1 = path.join(__dirname, "files", "1.txt");
  4. let filePath2 = path.join(__dirname, "files", "2.txt");
  5. let p1 = new Promise((resolve, reject)=>{
  6. //1、同步代码
  7. // console.log("同步代码");
  8. // pending(进行中)、fulfilled(已成功)和rejected(已失败)
  9. //2、异步代码
  10. fs.readFile(filePath1,"utf-8",(error1, data1)=>{
  11. if(error1){
  12. //失败的时候做的事情
  13. reject(error1);
  14. }
  15. //读取完之后做的事情
  16. resolve(data1)
  17. })
  18. });
  19. let p2 = new Promise((resolve, reject)=>{
  20. //1、同步代码
  21. // console.log("同步代码");
  22. // pending(进行中)、fulfilled(已成功)和rejected(已失败)
  23. //2、异步代码
  24. fs.readFile(filePath2,"utf-8",(error1, data1)=>{
  25. if(error1){
  26. //失败的时候做的事情
  27. reject(error1);
  28. }
  29. //读取完之后做的事情
  30. resolve(data1)
  31. })
  32. });
  33. Promise.all([p1,p2]).then((data)=>{
  34. // data是一个数组,分别是p1和p2最后传来的数据
  35. console.log(data); // [ '我', '爱' ]
  36. console.log(data.join("")); // 我爱
  37. }).catch((error)=>{
  38. //只要执行p1,p2时其中一个报错,就会执行这里的代码
  39. console.log(error);
  40. });

3.3、Promise类的race() 方法

参数:是一个数组,数组元素是Promise实例对象,只要数组里面的任何一个Promise成功了,则才会执行 then 第一个回调,且只执行1次。

  1. const fs = require("fs")
  2. const path = require("path")
  3. const util = require('util');
  4. let filePath1 = path.join(__dirname, "files", "1.txt")
  5. let filePath2 = path.join(__dirname, "files", "2.txt")
  6. let readFilePromise = util.promisify(fs.readFile);
  7. let p1 = readFilePromise(filePath1,"utf-8")
  8. let p2 = readFilePromise(filePath2,"utf-8")
  9. Promise.race([p1,p2]).then((data)=>{
  10. // p1,p2只要其中一个执行完,就会执行一遍这里的代码,且这里的代码只会执行1次
  11. console.log(123);
  12. console.log(data);
  13. });
  14. //123
  15. //我

四、async+await

4.1 async+await 终极版

看下面这一段代码:

  1. function func(a, b) {
  2. return a + b
  3. }
  4. let ret = func(10, 30);
  5. let ret1 = func(50, 320);
  6. let ret2 = func(1560, 30);
  7. let ret3 = func(10, 3560);
  8. console.log(ret + ret1 + ret2 + ret3);

如果Promise对象也能有对应的方式来接收,写成类似:

  1. let data1 = await readfilePromise; // 直接获取成功的数据

async 的最终格式如下:

  1. async function func() {
  2. let data1 = await promise对象1;
  3. let data2 = await promise对象2;
  4. let data3 = await promise对象3;
  5. }
  6. // 相当于让异步函数对象1先执行完毕之后,再执行异步函数对象2,再执行异步函数对象3

4.2、注意事项

  1. 如果await后面只写一个基本数据类型,会这个基本数据类型进行包装,包装成一个 Promise 对象
  1. async function func() {
  2. let data1 = await 123;
  3. //1. await后面只写一个基本数据类型 会这个基本数据类型进行包装,包装成一个 Promise 对象
  4. // 即data1相当于: new Promise((resolve,reject)=>{resolve(123)})
  5. console.log("data1:", data1); //data1: 123
  6. return data1
  7. // return await data1
  8. }
  9. let a = func();
  10. a.then((data)=>{
  11. console.log(data); //123 接收到上面的返回值Promise对象的执行结果
  12. });
  1. 如果await后面是一个 Promise,会把 resolve 的值返回
  2. async 函数里面的 await 是异步的,不是同步
  1. async function func() {
  2. console.log("start-------");
  3. let data1 = await readFile(filePath1, "utf-8");
  4. console.log("end-----------");
  5. let data2 = await readFile(filePath2, "utf-8");
  6. let data3 = await readFile(filePath3, "utf-8");
  7. console.log(data1+data2+data3);
  8. }
  9. console.log("start");
  10. func();
  11. console.log("end");
  12. //输出结果依次为:
  13. //start
  14. //start-------
  15. //end
  16. //end-----------
  1. 错误处理(外部处理)
  1. async function func() {
  2. let data1 = await readFile(filePath1, "utf-8");
  3. let data2 = await readFile(filePath2, "utf-8");
  4. let data3 = await readFile(filePath3, "utf-8");
  5. console.log(data1+data2+data3);
  6. // writeFile(filePath4, data1+data2+data3)
  7. }
  8. func().catch( error => {
  9. console.log(error);
  10. } ).finally(()=>{
  11. console.log("finally");
  12. });
  1. 错误处理(内部处理)
  1. async function func() {
  2. try{
  3. let data1 = await readFile(filePath1, "utf-8");
  4. let data2 = await readFile(filePath2, "utf-8");
  5. let data3 = await readFile(filePath3, "utf-8");
  6. }
  7. catch (error) {
  8. console.log(error);
  9. return
  10. }
  11. finally {
  12. console.log("finally");
  13. }
  14. console.log("end");
  15. }
  16. func();