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( ...
随机推荐
- Android apk签名详解——AS签名、获取签名信息、系统签名、命令行签名
Apk签名,每一个Android开发者都不陌生.它就是对我们的apk加了一个校验参数,防止apk被掉包.一开始做Android开发,就接触到了apk签名:后来在微信开放平台.高德地图等平台注册时,需要 ...
- C++走向远洋——(项目二、存储班长信息的学生类、派生)
*/ * Copyright (c) 2016,烟台大学计算机与控制工程学院 * All rights reserved. * 文件名:text.cpp * 作者:常轩 * 微信公众号:Worldhe ...
- .NET Core C#目录
.NET Core技术开发指南 简介 本系列教程是一份关于微软.NET Core技术栈的全面的教程,其中涉及了C#.Typescript.Angular.Redis等一系列的教程.其中教程中通常会含有 ...
- 从输入URL到页面展示
当我们输入 URL 并按回车后,浏览器会对 URL 进行检查,首先判断URL格式,比如是ftp http ed2k等等,我们这里假设这个URL是http://hellocassie.cn,那么浏览器会 ...
- I - A计划 HDU - 2102
A计划 Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submiss ...
- .NET Core 3.x之下的配置框架
一.配置框架的核心类库 首先我们使用.NET Core的配置框架需要安装额外的NuGet扩展包,下面是列举最常用的几个扩展包以及所对应的配置功能 NuGet Package Description M ...
- RedisTemplate:我不背锅,是你用错了
今天分享一个RedisTemplate的问题,感兴趣的可以继续看下去了,不感兴趣的继续撩妹去吧! 如下图:一位朋友给了我一个报错的图片,为啥为啥取不到值? 我也有点懵,第一反应就是RedisTempl ...
- iOS中使用block进行网络请求回调
iOS中使用block进行网络请求回调 HttpRequest.h // // HttpRequest.h // UseBlockCallBack // // Created by Michael o ...
- NLP(二十五)实现ALBERT+Bi-LSTM+CRF模型
在文章NLP(二十四)利用ALBERT实现命名实体识别中,笔者介绍了ALBERT+Bi-LSTM模型在命名实体识别方面的应用. 在本文中,笔者将介绍如何实现ALBERT+Bi-LSTM+CRF ...
- windows上用putty从linux上下载文件
我之前使用putty都是直接从网上下的putty.exe,其实如果想下载windows的mis二进制文件,系统安装的话会包含,pscp.psftp.puttygen等一系列的文件. 今天下从服务器上, ...