NodeJS 异步操作:摆脱‘Callback hell’初谈
来源:互联网 发布:淘宝代销下单好吗 编辑:程序博客网 时间:2024/05/23 10:23
众所周知,NodeJS具有的单线程,事件驱动,异步非阻塞IO模型,使得其在IO密集型程序,尤其是大型的Web服务中占有很大的优势。
下面就来谈谈几种NodeJS异步回调的实现。
最常规的一种是:
import fs from 'fs';fs.readFile(__dirname + '/a.txt', 'utf-8', (err, data) => { if(!err){ console.log(data); console.log('Finish reading'); } else console.log(err);})console.log('Start reading');
输出:
Start readingaaaFinish reading
如果用户想自定义回调函数代替 console.log(data)
可以再封装一层,将callback作为回调函数传入内部:
import fs from 'fs';const readFile = (path, callback) =>{ fs.readFile(__dirname + path, 'utf-8', (err, data) => { if(!err){ callback(data); console.log('Finish reading'); } else console.log(err); })}readFile('/a.txt', (data) => { console.log(`The content of file "a.txt" is: ${data}`)});console.log('Start reading');
输出:
Start readingThe content of file "a.txt" is aaaFinish reading
NodeJS将几乎所有的库函数实现为如下形式的异步回调机制
asyncFunc(arg0, arg1, ... (err, data) => { if(!err) callback(data);})
此时,假设我们以readFile函数为例,它实现了异步读取本地文件并在读取到内容后回调的功能。假如我们已经获取了当前路径某个文件夹下的3个文件[a.txt, b.txt, c.txt],需将顺次遍历和打印其内容,首先想到的是暴力和丑陋的多层回调方法如下:
import fs from 'fs';const read3Files = (callback) => { fs.readFile(__dirname + '/a.txt', 'utf-8', (err, data) => { if(!err){ callback('a.txt', data); fs.readFile(__dirname + '/b.txt', 'utf-8', (err, data) => { if(!err){ callback('b.txt', data); fs.readFile(__dirname + '/c.txt', 'utf-8', (err, data) => { if(!err){ callback('c.txt', data); console.log(`Done...`); } else console.log(err); }) } else console.log(err); }) } else console.log(err);})}read3Files( (file, data) => { console.log(`The content of file ${file} is: ${data}`)});console.log('Start reading');
输出:
Start readingThe content of file a.txt is: aaaThe content of file b.txt is: bbbThe content of file c.txt is: cccDone...
这就是臭名昭著的回调地狱了,回调函数层层嵌套,不仅缺乏美感,而且给Debug造成很大的困难。
而且这么做有个很大的弊端,读取的文件是硬编码的,如果给的文件列表数个很长的数组,就无法这么做了。
观察到上述代码的特性,我们可以将该其写成递归函数的形式如下,函数细节不再赘述,在当前文件夹下有5个txt文件如下,代码采用递归形式依次读取其内容。
import fs from 'fs';let fsList = ['/a.txt','/b.txt','/c.txt','/d.txt','/e.txt'];const readFileList = (fsList, i, callback, err, data) => { if(i > fsList.length) { console.log(`Done...`); return; } if(err) { console.log(err); return; } else if(data){ callback(fsList, i - 1, data); } fs.readFile(__dirname + fsList[i], 'utf-8', (err, data) => { return readFileList(fsList, i + 1, callback, err, data); });}readFileList(fsList, 0, (fsList, i, data) => {console.log(`The content of file ${fsList[i]} is: ${data}`)}, null, null);
输出:
Start readingThe content of file a.txt is: aaaThe content of file b.txt is: bbbThe content of file c.txt is: cccThe content of file /d.txt is: dddThe content of file /e.txt is: eeeDone...
这种方法看似简便了许多,但我们不想将时间浪费在自己实现递归函数这种难以Debug又容易产生各种错误的工作上,那么有没有
更为优雅的实现方式呢,在这里我们引入ES6和ES7的新特性,Promise
和Async, Await
关键词。
ES6 原生提供了 Promise 对象。所谓 Promise 对象,就是代表了某个未来才会知道结果的事件(通常是一个异步操作),并且这个事件提供统一的 API,可供进一步处理。
有了 Promise 对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise 对象提供的接口,使得控制异步操作更加容易。
let promise = new Promise((resolve, reject) => { if (/* 异步操作成功 */){ resolve(value); } else { reject(err); }});promise.then(value => { // success}.catch(err => { // fail})
因此对于readFile这个函数,或是你需要在程序中实现‘整体异步,内部链式调用‘的异步函数,我们可以将其封装为一个Promise对象,并将自定义callback传入then和catch函数:
import fs from 'fs';const readFile2 = (path) => { return new Promise((resolve, reject) => { fs.readFile(__dirname + path, 'utf-8', (err, data) => { if(!err) resolve(data); else reject(err); }) })}readFile2('/a.txt').then(data => console.log(data)).catch(err => console.log(err));##使用链式调用读取多个文件:readFile2('/a.txt').then(data => { console.log(data); return readFile2('/b.txt');}).then(data => { console.log(data); return readFile2('/c.txt');}).then(data => { console.log(data); return readFile2('/d.txt');}).then(data => { console.log(data); console.log(`Done...`);}).catch(err => console.log(err));
输出:
Start readingaaabbbcccdddDone...
封装Promise及链式调用的方法看上去很优雅,但其缺点也是硬编码,无法实现读取一个列表中的文件,
笔者暂时还没想到用递归调用的形式实现上述的链式函数。
在下一节中,笔者将引入 Async 和 Await 关键字来实现顺次以及非顺次读取一个FileList中的文件内容。
- NodeJS 异步操作:摆脱‘Callback hell’初谈
- 用Promise优化nodejs的callback hell
- Callback Hell-Javascript异步编程指导
- CALLBACK HELL
- Javascript 回调坑 CallBack Hell
- 对于nodejs的异步的callback理解
- 回调地狱,callback hell
- NodeJS callback示例
- nodejs 异步
- nodejs异步
- NodeJS 异步操作:事件队列的有序与并发操作
- Hell
- NodeJS 异步操作:更为优雅的实现:thenjs
- Android Http异步请求,Callback
- Android Http异步请求,Callback
- Callback异步回调接口
- hell--hell
- nodejs中callback的两个弱点
- ionic加载动画
- python3.6.0(Anaconda)安装PyQt5,“DLL load failed: 找不到指定的模块。”
- 选购和配置阿里云服务器(Java web版)
- 剑指Offer面试题9 & Leetcode70
- 为什么单例模式中要使用静态变量
- NodeJS 异步操作:摆脱‘Callback hell’初谈
- 用java做一个2048小游戏
- vs shfit+ctrl+f 全局搜索失
- 把GIT导出的项目转换成Maven项目
- mybatis系列三:springMVC和mybatis的运用
- 基于 Python 的数据结构与算法分析学习记录(6-9)—— 二叉堆操作
- Java信号量Semaphor解析
- unity如何设置fog,shader如何接收fog
- Ubuntu下安装gsoap和onvif的文件生成