promise的优势
通过不同的方式读取在 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的优势的更多相关文章
- 大白话讲解Promise(一)
去年6月份, ES2015正式发布(也就是ES6,ES6是它的乳名),其中Promise被列为正式规范.作为ES6中最重要的特性之一,我们有必要掌握并理解透彻.本文将由浅到深,讲解Promise的基本 ...
- Promise学习
转自:http://www.cnblogs.com/lvdabao/p/es6-promise-1.html 去年6月份, ES2015正式发布(也就是ES6,ES6是它的乳名),其中Promise被 ...
- 你不知道的JavaScript--大白话讲解Promise
转载:http://blog.csdn.net/i10630226/article/details/50867792 一.Promise小试 复杂的概念先不讲,我们先简单粗暴地把Promise用一下, ...
- JS 中Promise 模式
异步模式在web编程中变得越来越重要,对于web主流语言Javscript来说,这种模式实现起来不是很利索,为此,许多Javascript库(比如 jQuery和Dojo)添加了一种称为promise ...
- Es6 Promise 用法详解
Promise是什么?? 打印出来看看 console.dir(Promise) 这么一看就明白了,Promise是一个构造函数,自己身上有all.reject.resolve这几个眼熟的方 ...
- ES6 中 Promise 详解
Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果.从语法上说,Promise 是一个对象,从它可以获取异步操作的消息.Promise 提供统一的 API ...
- ES6中Promise详解
Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果.从语法上说,Promise 是一个对象,从它可以获取异步操作的消息. Promise 提供统一的 AP ...
- 【转】大白话讲解Promise(一)
原文地址:https://www.cnblogs.com/lvdabao/p/es6-promise-1.html ES6 Promise 先拉出来遛遛 复杂的概念先不讲,我们先简单粗暴地把Promi ...
- ES6 Promise 用法讲解
Promise是一个构造函数,自己身上有all.reject.resolve这几个眼熟的方法,原型上有then.catch等同样很眼熟的方法. 那就new一个 var p = new Promise( ...
随机推荐
- AI超越人类大脑,或许是场“别有用心者”的骗局
谷歌.微软.苹果.特斯拉.百度.腾讯.阿里等互联网巨头企业,以及纳德拉.马斯克.扎克伯格.马云等互联网大佬,近年来一直都对人工智能--AI非常上心.在众多场合对AI给予了或肯定,或恐惧的评价.但无 ...
- MAC使用nginx分发80至8080端口
由于项目必须要启动80端口,但是mac系统中非root用户无法直接使用1024以下的端口 2.释放apache的80端口 由于Mac OS是自带Apache服务的,它本身占用了80端口,首先你需要将A ...
- dagger2的Qualifier与Scope
Qualifier即Named 当module的@Provides提供相同变量的不同属性时:用于区分把哪一个初始化 Module 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ...
- WiredTiger运行时参数优化
MongoDB的WiredTiger存储引擎,用了一段时间,遇到了一些问题,通过优化WT参数,也解决了一些问题,做个小结. cache_size 指定WT存储引擎内部cache的内存用量上限. 需要注 ...
- C++扬帆远航——12(抓小偷)
/* * Copyright (c) 2016,烟台大学计算机与控制工程学院 * All rights reserved. * 文件名:zhaoxiaotou.cpp * 作者:常轩 * 微信公众号: ...
- Redis list实现原理 - 双向循环链表
双向链表 双向表示每个节点知道自己的直接前驱和直接后继,每个节点需要三个域 查找方向可以是从左往右也可以是从右往左,但是要实现从右往左还需要终端节点的地址,所以通常会设计成双向的循环链表; 双向的循环 ...
- USB小白学习之路(6) IIC EEPROM读取解析
IIC EEPROM读取解析 1. 编译错误处理(这里可以忽略) 在解压包解压了程序后,直接编译,出现如下错误. *** WARNING L14: INCOMPATIBLE MEMORY MODEL ...
- [红日安全]Web安全Day4 - SSRF实战攻防
本文由红日安全成员: MisakiKata 编写,如有不当,还望斧正. 大家好,我们是红日安全-Web安全攻防小组.此项目是关于Web安全的系列文章分享,还包含一个HTB靶场供大家练习,我们给这个项目 ...
- Spring事务Transactional和动态代理(一)-JDK代理实现
系列文章索引: Spring事务Transactional和动态代理(一)-JDK代理实现 Spring事务Transactional和动态代理(二)-cglib动态代理 Spring事务Transa ...
- NSURLSession的前世今生
系统网络框架架构图 前世-NSURLConnection NSURLConnection是苹果提供的原生网络访问类,已经有10多年的历史了,它从 iOS 2.0 开始,一直到iOS9被废弃.异步方法在 ...