越努力,越幸运,做个ccode~

0%

Promise解决回调地狱问题

回调地狱问题的由来

需求: 封装一个方法,给定文件路径,可以读取文件内容,并把文件内容返回,如果错误返回错误结果

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,就调用第二个回调函数。