通过不同的方式读取在 files 文件夹下的三个文件来引出 promise 在处理异步时与回调函数相比的优势,files 文件夹有三个文件 a.json,b.json,c.json。

// a.json
{
"content": "this is a.json",
"next": "b.json"
}
// b.json
{
"content": "this is b.json",
"next": "c.json"
}
// c.json
{
"content": "this is c.json",
"next": null
}

现在要依次读取这三个文件,并且 b.json 的文件名要通过 a.json 文件中的 next 属性获得,c.json 的文件名要通过 b.json 的文件名获得。

首先我们先来看一下读取文件时输出内容的格式

const fs = require('fs')
const path = require('path') // 回调函数且不封装为函数的方式
const fullFileName = path.resolve(__dirname, 'files', 'a.json')
fs.readFile(fullFileName, (err, data) => {
console.log(data)
})

从文件中直接读取出来的是二进制的形式

data 是二进制形式

data 转换为字符串

console.log(data.toString());

data 转换为对象

console.log(JSON.parse(data.toString());

现在我们用最符合人思维方式的写法来一次读取三个文件的内容。

// 回调函数且不封装为函数的方式
const fullFileName = path.resolve(__dirname, 'files', 'a.json')
// 读取a.json
fs.readFile(fullFileName, (err, data) => {
console.log(JSON.parse(data.toString()))
const fileName = JSON.parse(data.toString()).next
const fullFileName = path.resolve(__dirname, 'files', fileName)
// 从a.json中获得b.json文件名,然后读取b.json
fs.readFile(fullFileName, (err, data) => {
console.log(JSON.parse(data.toString()))
const fileName = JSON.parse(data.toString()).next
const fullFileName = path.resolve(__dirname, 'files', fileName)
// 从b.json中获取c.json文件名,然后读取c.json
fs.readFile(fullFileName, (err, data) => {
console.log(JSON.parse(data.toString()))
})
})
})

读取结果:

上面这样写代码复用性很低,我们可以考虑将读取文件内容封装为一个函数,这样每次读取文件内容时直接调用那个函数就可以了。

// 将读取文件内容封装成一个函数
function readFileContent(fileName) {
fs.readFile(fileName, (err, data) => {
console.log(JSON.parse(data.toString()))
})
}
// 读取a.json的内容
const fullFileName = path.resolve(__dirname, 'files', 'a.json')
readFileContent(fullFileName)

读取结果:

如果我们想要完成连续读取三个文件,并且下一个文件的文件名来自上一个文件,上面封装的函数显然是不能满足要求的。

上面回调函数的内容是 console.log(JSON.parse(data.toString())),这样写死的显然不能再读取下一个文件,如果我们将 readFileContent 的第二个参数变成一个函数,然后在回调函数中调用执行,那么在这个函数中我们就可以再次读取下一个文件。

// 封装连续读取文件的函数
function readFileContent(fileName, callback) {
const fullFileName = path.resolve(__dirname, 'files', fileName)
fs.readFile(fullFileName, (err, data) => {
// 这里使用callback时需要传递一个参数,那么定义的callback函数也有一个参数
callback(JSON.parse(data.toString()))
})
} const fileName = 'a.json'
readFileContent(fileName, aData => {
console.log(aData);
// 获取b.json的名称
const fileName = aData.next;
// 读取b.json
readFileContent(fileName, bData => {
console.log(bData)
// 获取c.json的名称
const fileName = bData.next
// 读取c.json
readFileContent(fileName, cData => {
console.log(cData)
})
})
})

像上面这样写如果需要读取的文件继续增多,那么回调函数就会一直增加下去,呈现金字塔的形状,函数中间嵌套着函数,导致代码可读性较低,这也就是经常说的回调地狱。

关于回调地狱推荐这篇博文,讲的很清楚,回调地狱

解决回调地狱一种比较常用的方法就是使用 promise,关于 promise 的知识在这里就不多说了,现在利用 promise 读取一个文件的内容。

const promise = new Promise((resolve, reject) => {
const fullFileName = path.resolve(__dirname, 'files', 'a.json');
fs.readFile(fullFileName, (err, data) => {
if (err) {
reject(err)
} else {
resolve(data)
}
})
}) promise.then((data) => {
console.log(JSON.parse(data.toString()))
}, (err) => {
console.log(err)
})

读取结果

这样写很显然不能完成多个文件的读取,我们现在也考虑将其封装为一个函数,如果让这个函数返回一个 promise 那么调用一次就返回一个 promise,这样就可以多次读取文件了。

// 封装函数利用promise读取三个文件的内容
function readFileContent(fileName) {
const fullFileName = path.resolve(__dirname, 'files', fileName)
return new Promise((resolve, reject) => {
fs.readFile(fullFileName, (err, data) => {
if (err) {
reject(err)
} else {
resolve(data)
}
})
})
} const fileName = 'a.json'
readFileContent(fileName).then((data) => {
console.log(JSON.parse(data.toString()));
const fileName = JSON.parse(data.toString()).next;
return readFileContent(fileName)
}).then((data) => {
console.log(JSON.parse(data.toString()));
const fileName = JSON.parse(data.toString()).next;
return readFileContent(fileName)
}).then((data) => {
console.log(JSON.parse(data.toString()));
})

读取结果:

重点在于第19行和23行的代码,当在 then 中返回一个新的 promise 时,下一个 then 中的 data 就是这个新的 promise 中 resolve(data) 的参数 data,then 响应的是这个新的 promise。

可以看到当使用 promise 时,不会再出现函数嵌套的情况了,每个 then 都是一个异步操作,条理也比较清晰,因此 promise 也作为一种解决回调地狱比较常见的方式,解决回调地狱更多的方法可以参考上面推荐的那篇博客。

完,如有不恰当之处,还望告知,感谢。

promise的优势的更多相关文章

  1. 大白话讲解Promise(一)

    去年6月份, ES2015正式发布(也就是ES6,ES6是它的乳名),其中Promise被列为正式规范.作为ES6中最重要的特性之一,我们有必要掌握并理解透彻.本文将由浅到深,讲解Promise的基本 ...

  2. Promise学习

    转自:http://www.cnblogs.com/lvdabao/p/es6-promise-1.html 去年6月份, ES2015正式发布(也就是ES6,ES6是它的乳名),其中Promise被 ...

  3. 你不知道的JavaScript--大白话讲解Promise

    转载:http://blog.csdn.net/i10630226/article/details/50867792 一.Promise小试 复杂的概念先不讲,我们先简单粗暴地把Promise用一下, ...

  4. JS 中Promise 模式

    异步模式在web编程中变得越来越重要,对于web主流语言Javscript来说,这种模式实现起来不是很利索,为此,许多Javascript库(比如 jQuery和Dojo)添加了一种称为promise ...

  5. Es6 Promise 用法详解

     Promise是什么??    打印出来看看  console.dir(Promise) 这么一看就明白了,Promise是一个构造函数,自己身上有all.reject.resolve这几个眼熟的方 ...

  6. ES6 中 Promise 详解

    Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果.从语法上说,Promise 是一个对象,从它可以获取异步操作的消息.Promise 提供统一的 API ...

  7. ES6中Promise详解

    Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果.从语法上说,Promise 是一个对象,从它可以获取异步操作的消息. Promise 提供统一的 AP ...

  8. 【转】大白话讲解Promise(一)

    原文地址:https://www.cnblogs.com/lvdabao/p/es6-promise-1.html ES6 Promise 先拉出来遛遛 复杂的概念先不讲,我们先简单粗暴地把Promi ...

  9. ES6 Promise 用法讲解

    Promise是一个构造函数,自己身上有all.reject.resolve这几个眼熟的方法,原型上有then.catch等同样很眼熟的方法. 那就new一个 var p = new Promise( ...

随机推荐

  1. JasperReport框架使用教程(附带常见空白页问题说明)

    概述与安装使用 1. PDF报表概述 概述 ​ 在企业级应用开发中,报表生成.报表打印下载是其重要的一个环节.在之前的课程中我们已经学习了报表中比较重要的一种:Excel报表.其实除了Excel报表之 ...

  2. 量化投资学习笔记31——《Python机器学习应用》课程笔记05

    用分类算法进行上证指数涨跌预测. 根据今天以前的150个交易日的数据,预测今日股市涨跌. 交叉验证的思想:将数据集D划分为k个大小相似的互斥子集,每个子集都尽可能保持数据分布的一致性,即从D中通过分层 ...

  3. JDBC大数据的采取

    ## JDBC的大类型数据的存取 ## # 基本概念: |-- 大文本类型数据和大二进制数据: 主要思想用于将大型的二进制数据(字节) 或是大型的文本数据(字符)从磁盘文件中读取 到数据库中,或是从数 ...

  4. 一篇文章彻底说清JS的深拷贝/浅拷贝

    一篇文章彻底说清JS的深拷贝and浅拷贝 这篇文章的受众 第一类,业务需要,急需知道如何深拷贝JS对象的开发者. 第二类,希望扎实JS基础,将来好去面试官前秀操作的好学者. 写给第一类读者 你只需要一 ...

  5. 前端javascript知识(一)

    介绍一下 JS 的基本数据类型. Undefined.Null.Boolean.Number.String 介绍一下 JS 有哪些内置对象. Object 是 JavaScript 中所有对象的父对象 ...

  6. #AcWing系列课程Level-2笔记——5.高精度“+”算法

    高精度"+"算法 编写高精度"+",记住下面的过程,代码也就游刃有余了! 1.首先我们要明白大整数是如何存储的? 2.其次存储完,如何运算? 高精度" ...

  7. java线程间的协作

    本次内容主要讲等待/通知机制以及用等待/通知机制手写一个数据库连接池. 1.为什么线程之间需要协作 线程之间相互配合,完成某项工作,比如:一个线程修改了一个对象的值,而另一个线程感知到了变化,然后进行 ...

  8. java并发编程基础概念

    本次内容主要讲进程和线程.CPU核心数和线程数.CPU时间片轮转机制.上下文切换,并行和并发的基本概念以及并发编程的好处和注意事项,为java并发编程打下扎实基础. 1.什么是进程和线程 1.1 进程 ...

  9. 0312 java接口测试三棱军刺rest-assured

    背景 java程序员一般写的是后端服务是JavaWeb类型的项目,主要包括Http接口和dubbo接口,Http接口一般采用的rest风格,那么如何快速的对rest接口在第三方的测试框架上进行测试呢? ...

  10. Vue Snackbar 消息条队列显示,依次动画消失的实现

    效果预览 思路 封装 Snackbar 组件: 在根路由页面下建立全局 Snackbar 控制器,统一管理 Snackbar: 通过事件通知全局 Snackbar 控制器显示消息: 实现 1. 封装 ...