回调地狱问题的由来
需求: 封装一个方法,给定文件路径,可以读取文件内容,并把文件内容返回,如果错误返回错误结果
1 |   const fs = require('fs') | 
2 |   const path = require('path') | 
3 | |
4 |   // 普通读取文件的方式 | 
5 |   function getFileByPath(fpath){ | 
6 |     fs.readFile(fpath,'utf-8',(err,data) => { | 
7 |       if(err) throw err | 
8 |       console.log(data) | 
9 |       return data | 
10 |     }) | 
11 |   } | 
12 | |
13 |   let result = getFileByPath(path.join(__dirname,'.files/1.txt')) | 
14 |   console.log(result) | 
15 | ``` | 
16 |  | 
17 |  | 
18 | 这样确实可以读到文件内容,但是`result`却是`undefined`,因为`readFiles`是异步读取的 | 
19 |  | 
20 | 于是想到在调用`getFileByPath`时添加回调函数 | 
21 |  | 
22 | ```js | 
23 |   function getFileByPath(fpath,callback){ | 
24 |     fs.readFile(fpath,'utf-8',(err,data) => { | 
25 |       if(err) throw err | 
26 |       callback(data) | 
27 |     }) | 
28 |   } | 
29 | |
30 |   getFileByPath(path.join(__dirname,'.files/1.txt'),(data)=>{ | 
31 |     console.log(data) | 
32 |   }) | 
33 | ``` | 
34 |  | 
35 | 但是如果error了,`readFile`里的`callback`就不执行了, | 
36 | 需要将成功的结果和失败的结果都返回给用户,所以添加两个回调函数,一个成功调用`succCb`,一个失败调用`errCb` | 
37 |  | 
38 | ```js | 
39 |   function getFileByPath(fpath,succCb, errCb){ | 
40 |     fs.readFile(fpath,'utf-8',(err,data) => { | 
41 |       if(err) return errCb | 
42 |       succCb | 
43 |     }) | 
44 |   } | 
45 | |
46 |   getFileByPath(path.join(__dirname,'.files/1.txt'),(data)=>{ | 
47 |     console.log(data) | 
48 |   },(err)=>{ | 
49 |     console.log(err) | 
50 |   }) | 
51 | ``` | 
52 |  | 
53 | 看起来很完美呢,如果这时候再加个需求呢,读取文件1成功后,再读取文件2,读取文件2成功后再读取文件3,那么代码将会是下面的样子 | 
54 |  | 
55 |     getFileByPath(path.join(__dirname,'.files/1.txt'),(data1)=>{ | 
56 |       console.log(data1) | 
57 |  | 
58 |       // 读取文件2 | 
59 |       getFileByPath(path.join(__dirname,'files/2.txt'),(data2)=>{ | 
60 |         console.log(data2) | 
61 |  | 
62 |         // 读取文件3 | 
63 |         getFileByPath(path.join(__dirname,'files/3.txt'),(data3)=>{ | 
64 |           console.log(data3) | 
65 |         }) | 
66 |       }) | 
67 |     }) | 
68 |  | 
69 | 哈哈,层层缩进,如果后面还要读取很多文件呢,那么就陷入了回调地狱,看看下面这张图,很形象有木有 | 
70 |  | 
71 | {% asset_img callback_hell.jpg 回调地狱 %} | 
72 |  | 
73 | ## 用Promise解决回调地狱问题 | 
74 |  | 
75 | 如果不想让代码变成酱紫,我们就开始使用Promise,其实Promise的本质就是单纯为了解决回调地狱问题,它会让代码变的优雅,并不会减少代码量 | 
76 |  | 
77 |     > console.log(Promise) | 
78 |       ƒ Promise() { [native code] } | 
79 |  | 
80 | 可以看出来Promise是一个构造函数,我们通过new Promise可以生成一个Promise的实例 | 
81 |  | 
82 | 关于Promise更详细的说明 [Promise](http://es6.ruanyifeng.com/?search=fetch&x=0&y=0#docs/promise) | 
83 |  | 
84 | 如果用Promise来实现读取三个文件的需求呢 | 
85 |  | 
86 | 首先构建一个方法,返回一个promise | 
87 |  | 
88 |     function getFileByPath(fpath){ | 
89 |       var promise = new Promise(resolve,reject){ | 
90 |         fs.readFile(fpath,'utf-8',(err,data)=>{ | 
91 |           if(err) return reject(err) | 
92 |           resolve(data) | 
93 |         }) | 
94 |       } | 
95 |       return promise | 
96 |     } | 
97 |  | 
98 | 开始读取文件 | 
99 |  | 
100 |     getFileByPath('files1.txt').then((data1)=>{ | 
101 |       console.log(data1) | 
102 |  | 
103 |       getFileByPath('files2.txt').then((data2)=>{ | 
104 |         console.log(data2) | 
105 |  | 
106 |         getFileByPath('files3.txt').then((data3)=>{ | 
107 |           console.log(data3) | 
108 |         }) | 
109 |       }) | 
110 |     }) | 
111 |  | 
112 | 咦,写完了,这好像还是回调地狱啊,纳尼? | 
113 |  | 
114 |     getFileByPath('files1.txt').then( data1 => { | 
115 |       console.log(data1) | 
116 |       return getFileByPath('files2.txt') | 
117 |     }).then( data2 => { | 
118 |       console.log(data2) | 
119 |       return getFileByPath('file3.txt') | 
120 |     }).then( data3 => { | 
121 |       console.log(data3) | 
122 |     }) | 
123 |  | 
124 | emmmmm...,正确打开方式是这样,是不是很优雅 | 
125 |  | 
126 | 但是为什么第二个`then`会获取到`getFileByPath('files2.txt')`的结果呢? | 
127 |  | 
128 | > 上面代码中,第一个then方法指定的回调函数,返回的是另一个Promise对象。这时,第二个then方法指定的回调函数,就会等待这个新的Promise对象状态发生变化。如果变为resolved,就调用第一个回调函数,如果状态变为rejected,就调用第二个回调函数。 |