第三章 Promise 的优势
1 指定回调函数的方式更加灵活
旧的:必须在启动异步任务前指定。
Promsie:启动异步任务 => 返回 Promise 对象 => 给 Promise 对象绑定回调函数(可以在异步任务完成后指定回调函数,甚至可以指定多个回调函数)。
// 成功的回调函数
function successCallback(result) {
console.log("声音文件创建成功: " + result);
}
// 失败的回调函数
function failureCallback(error) {
console.log("声音文件创建失败: " + error);
}
/* 1.1 使用纯回调函数 (旧的方式) */
createAudioFileAsync(audioSettings, successCallback, failureCallback)
/* 1.2. 使用Promise */
const promise = createAudioFileAsyncPromise(audioSettings); // 异步任务已经执行
promise.then(successCallback, failureCallback); // 通过调用 then 方法才能执行回调函数
Promise 方式我们可以在任意时刻执行回调函数,哪怕异步任务已经完成了一段时间
// 返回 Promise 对象,此时异步任务已经执行
const promise = createAudioFileAsync(audioSettings);
// 5s 之后再执行回调函数,哪怕这个时候可能异步已经完成一段时间了
setTimeout(() => {
promise.then(successCallback, failureCallback);
}, 5000);
Promise 对象通过多次调用 then 方法,就可以指定多个回调函数,它们会按照插入顺序执行。
// 返回 Promise 对象
const promise = createAudioFileAsync(audioSettings);
// 通过 then 方法指定回调函数
promise.then(successCallback, failureCallback);
// 再次通过 then 方法指定回调函数
promise.then(successCallback1, failureCallback1);
案例:读取文件
const util = require('util');
const fs = require('fs');
// 读取文件返回 Promise 对象
let result = util.promisify(fs.readFile)('./content.txt');
setTimeout(() => {
result.then((data) => {
console.log('至少5s之后读取文件内容:' + data);
})
}, 5000);
result.then((data) => {
console.log('读取文件内容:' + data);
});
result.then((data) => {
console.log('再次文件内容:' + data);
});
2 支持链式调用,可以解决回调地狱问题
1. 什么是回调地狱?
回调函数嵌套调用,外部异步执行的结果是嵌套异步操作的条件。
2. 回调地狱的缺点?
- 不便于阅读
- 不便于异常处理
3. 解决方案?
Promsie 链式调用
4. 终极解决方案?
async/await
/*
1. 回调地狱
*/
doSomething(function(result) {
doSomethingElse(result, function(newResult) {
doThirdThing(newResult, function(finalResult) {
console.log('Got the final result: ' + finalResult)
}, failureCallback)
}, failureCallback)
}, failureCallback)
/*
2. 使用promise的链式调用解决回调地狱
*/
doSomething().then(function(result) {
return doSomethingElse(result)
})
.then(function(newResult) {
return doThirdThing(newResult)
})
.then(function(finalResult) {
console.log('Got the final result: ' + finalResult)
})
.catch(failureCallback)
/*
3. async/await: 回调地狱的终极解决方案
*/
async function request() {
try {
const result = await doSomething()
const newResult = await doSomethingElse(result)
const finalResult = await doThirdThing(newResult)
console.log('Got the final result: ' + finalResult)
} catch (error) {
failureCallback(error)
}
}
案例:按顺序读取多个个文件
/**
* 因为读取文件是异步操作,必须在读取完第一个文件之后再去读取第二个文件,才能实现按照顺序读取。
* 旧的写法必然造成回调地狱
*/
const fs = require('fs');
fs.readFile('./content1.txt', (err, data)=>{
if (err) throw err;
console.log('第一个文件内容:' + data);
fs.readFile('./content2.txt', (err, data)=>{
if (err) throw err;
console.log('第二个文件内容:' + data);
fs.readFile('./content4.txt', (err, data)=>{
if (err) throw err;
console.log('第三个文件内容:' + data);
});
});
});
/**
* Promise 对象链式调用 then 方法,解决回调地狱。
*/
const util = require('util');
const fs = require('fs');
const readFile = util.promisify(fs.readFile);
readFile('./content1.txt').then(data => {
console.log('第一个文件内容:' + data);
return readFile('./content2.txt');
}).then(data => {
console.log('第二个文件内容:' + data);
return readFile('./content3.txt');
}).then(data => {
console.log('第三个文件内容:' + data);
}).catch(error => {
console.log('读取失败:' + error);
});