Node

一个 promise 在在代码中体现出来就是一个对象,该对象一般保存一个异步执行的任务。

Promise是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6将其写进了语言标准,统一了用法,原生提供了 Promise 对象。表示是一个未来的事情,可能做到,也可能失败。 Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。

Promise 对象

从语法上说,Promise是一个对象,从它可以获取异步操作的结果。

Promise对象代表一个异步操作,有三种状态:

  • Pending(进行中)
  • Resolved(已完成,又称Fulfilled)
  • 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
const fs = require('fs');


const p = new Promise((resolve, reject) => {
console.log('Promise 容器执行了');
fs.readFile('./es6.md', 'utf8', (err, data) => {
if (err) {


return reject(err);
}
// 容器中异步任务成功之后,就调用 Promise 容器中的 resolve 方法,
// 将结果传递给 resolve 方法
resolve(data);
});
});
// 当 Promise 对象一经创建就会立即执行
// 可以通过 Promise 容器对象的任何方法接收 容器中 resolve 传递的结果值
// then 方法需要接收一个回调函数,该回调函数是 Promise 容器中的 resolve
p.then(data => {
console.log(data);
}, err => {
console.log('读取文档失败了');
});

解决异步调用问题

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
const fs = require('fs');
const p1 = new Promise((resolve, reject) => {
fs.readFile('./data/01.txt', 'utf8', (err, data) => {
if (err) {
return reject(err);
}
resolve(data);
});
});
const p2 = new Promise((resolve, reject) => {
fs.readFile('./data/02.txt', 'utf8', (err, data) => {
if (err) {
return reject(err);
}
resolve(data);
});
});
const p3 = new Promise((resolve, reject) => {
fs.readFile('./data/03.txt', 'utf8', (err, data) => {
if (err) {
return reject(err);
}
resolve(data);
});
});
p1.then(data => {
console.log(data);
return p2;
}).then(data => {
// 当前这个 then 里面的回调函数的参数,就是上一个 then 中回调函数的返回值
// 该返回值有 3 种情况,
// 1, 没有返回值就是 undefined
// 2. 手动的 return 普通值
// 3. 返回一个新的 Promise 对象
// 如果是 Promise 对象,那么当前 then 就是 该 Promise 对象 resolve 函数的结果
console.log(data);
return p3;
}).then(data => {
console.log(data);
}).catch(err => {
// 当前这个catch 方法就可以把之前所有的任务中可能出现的异常都捕获
// 甚至包括 then 函数中的异常也可以捕获到
console.log(err);
});

封装 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
26
const fs = require('fs');
function (filePath, encoding) {
return new Promise((resolve, reject) => {
fs.readFile(filePath, encoding, (err, data) => {
if (err) {
return reject(err);
}
resolve(data);
});
});
}
readFile('./data/01.txt', 'utf8')
.then(data => {
console.log(data);
return readFile('./data/02.txt', 'utf8');
})
.then(data => {
console.log(data);
return readFile('./data/03.txt', 'utf8');
})
.then(data => {
console.log(data);
})
.catch(err =>{
console.log(err);
});

封装 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
26
27
28
29
30
31
const fs = require('fs');
function (filePath) {
return new Promise((resolve, reject) => {
fs.readFile(filePath, (err, data) => {
if (err) {
return reject(err);
}
resolve(data);
});
});
}
function writeFile(filePath, data) {
return new Promise((resolve, reject) => {
fs.writeFile(filePath, data, err => {
if (err) {
return reject(err);
}
resolve();
});
});
}
readFile('./data/01.txt')
.then(data => {
return writeFile('./data/01copy.txt', data);
})
.then(() => {
console.log(write success);
})
.catch(err =>{
console.log(err);
});

基本使用

Promise.all()

Promise.all方法用于将多个Promise实例,包装成一个新的Promise实例。

1
2
3
4
var p = Promise.all([p1, p2, p3]);
p.then(data => {
console.log(data); // data 是一个数组
})

Promise.all方法接受一个数组作为参数,p1、p2、p3都是Promise对象的实例。

p的状态由p1、p2、p3决定,分成两种情况:

  • 只有p1、p2、p3的状态都变成fulfilled ,p的状态才会变成fulfilled

    • 此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
  • 只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected

    • 此时第一个被reject的实例的返回值,会传递给p的回调函数。
      封装异步并行读取文档 Promise 的另外一个版本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const fs = require('fs');
function readFile(filePath) {
return new Promise((resolve, reject) => {
fs.readFile(filePath, (err, data) => {
if (err) {
return reject(err);
}
resolve(data);
});
});
}
// Promise 构造函数的 all() 方法接收一个每个元素都是 Promise 对象的数组
// 返回结果也是 Promise 对象
// Promise 专门用来解决回调地狱的,也可以做异步流程控制
Promise.all([
readFile('./data/01.txt'),
readFile('./data/02.txt'),
readFile('./data/03.txt')
]).then(data => {
console.log(data[0].toString());
console.log(data[1].toString());
console.log(data[2].toString());
});

以读文档为例子,封装 Promise API:

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
26
27
28
29
const fs = require('fs');
function readFile(filePath) {
return new Promise((resolve, reject) => {
fs.readFile(filePath, (err, data) => {
if (err) {
return reject(err);
}
resolve(data);
});
});
}
// 提供一个方法,该方法接收一个数组,数组中的指定要读取的文档路径,个数不定
// 当所有文档读取到的时候,将读取到的所有文档的结果返回
// getAll(['./data/01.txt', './data/02.txt', './data/03.txt']).then().catch()
function getAll(filePaths) {
return Promise.all(filePaths.map(filePath => readFile(filePath)));
}
getAll([
'./data/01.txt',
'./data/02.txt',
'./data/03.txt'
]).then(data => {
console.log(data[0].toString());
console.log(data[1].toString());
console.log(data[2].toString());
})
.catch(err => {
console.log('读取文档失败');
});

Promise.race()

Promise.race方法同样是将多个Promise实例,包装成一个新的Promise实例。

1
var p = Promise.race([p1,p2,p3]);

上面代码中,只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的Promise实例的返回值,就传递给p的回调函数。

并行执行一系列的异步任务,只要有一个异步任务完成,那业务就完成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const fs = require('fs');
function readFile(filePath) {
return new Promise((resolve, reject) => {
fs.readFile(filePath, (err, data) => {
if (err) {
return reject(err);
}
resolve(data);
});
});
}
// 谁先完成得到谁的数据
Promise.race([readFile('./data/01.txt'), readFile('./data/02.txt')])
.then(data => {
console.log(data.toString());
})