目前有几个比较好的解决方法前端
fs.readFile('./sample.txt', 'utf-8', (err, content) => {
let keyword = content.substring(0, 5);
db.find(`select * from sample where kw = ${keyword}`, (err, res) => {
get(`/sampleget?count=${res.length}`, data => {
console.log(data);
});
});
});
复制代码
以上代码包括了三个异步操做:node
咱们每增长一个异步请求,就会多添加一层回调函数的嵌套,这样下去,可读性会愈来愈低,也不易于之后的代码维护。过多的回调也就让咱们陷入“回调地狱”。接下来会大概介绍一下规避回调地狱的方法。es6
回调嵌套所带来的一个重要的问题就是代码不易阅读与维护。由于广泛来讲,过多的嵌套(缩进)会极大的影响代码的可读性。基于这一点,能够进行一个最简单的优化----将各个步骤拆解为单个functionweb
//HTTP请求
function getData(count) {
get(`/sampleget?count=${count}`, data => {
console.log(data);
});
}
//查询数据库
function queryDB(kw) {
db.find(`select * from sample where kw = ${kw}`, (err, res) => {
getData(res.length);
});
}
//读取文件
function readFile(filepath) {
fs.readFile(filepath, 'utf-8', (err, content) => {
let keyword = content.substring(0, 5);
queryDB(keyword);
});
}
//执行函数
readFile('./sample.txt');
复制代码
经过改写,再加上注释,能够很清晰的知道这段代码要作的事情。该方法很是简单,具备必定的效果,可是缺乏通用性。数据库
addEventListener应该不陌生吧,若是你在浏览器中写过监听事件。 借鉴这个思路,咱们能够监听某一件事情,当事情发生的时候,进行相应的回调操做;另外一方面,当某些操做完成后,经过发布事件触发回调。这样就能够将本来捆绑在一块儿的代码解耦。编程
const events = require('events');
const eventEmitter = new events.EventEmitter();
eventEmitter.on('db', (err, kw) => {
db.find(`select * from sample where kw = ${kw}`, (err, res) => {
eventEmitter('get', res.length);
});
});
eventEmitter.on('get', (err, count) => {
get(`/sampleget?count=${count}`, data => {
console.log(data);
});
});
fs.readFile('./sample.txt', 'utf-8', (err, content) => {
let keyword = content.substring(0, 5);
eventEmitter. emit('db', keyword);
});
复制代码
events 模块是node原生模块,用node实现这种模式只须要一个事件发布/监听的库。小程序
Promise是es6的规范 首先,咱们须要将异步方法改写成Promise,对于符合node规范的回调函数(第一个参数必须是Error), 可使用bluebird的promisify方法。该方法接受一个标准的异步方法并返回一个Promise对象微信小程序
const bluebird = require('bluebird');
const fs = require("fs");
const readFile = bluebird.promisify(fs.readFile);
复制代码
这样fs.readFile就变成一个Promise对象。 可是可能有些异步没法进行转换,这样咱们就须要使用原生Promise改造。 以fs.readFile为例,借助原生Promise来改造该方法:promise
const readFile = function (filepath) {
let resolve,
reject;
let promise = new Promise((_resolve, _reject) => {
resolve = _resolve;
reject = _reject;
});
let deferred = {
resolve,
reject,
promise
};
fs.readFile(filepath, 'utf-8', function (err, ...args) {
if (err) {
deferred.reject(err);
}
else {
deferred.resolve(...args);
}
});
return deferred.promise;
}
复制代码
咱们在方法中建立一个Promise对象,并在异步回调中根据不一样的状况使用reject与resolve来改变Promise对象的状态。该方法返回这个Promise对象。其余的一些异步方法能够参照这种方式进行改造。 假设经过改造,readFile、queryDB与getData方法均会返回一个Promise对象。代码就会变成这样:浏览器
readFile('./sample.txt').then(content => {
let keyword = content.substring(0, 5);
return queryDB(keyword);
}).then(res => {
return getData(res.length);
}).then(data => {
console.log(data);
}).catch(err => {
console.warn(err);
});
复制代码
经过then的链式改造。使代码的整洁度在必定的程度上有了一个较大的提升。
generator是es6中的一个新的语法。在function关键字后添加*便可将函数变为generator。
const gen = function* () {
yield 1;
yield 2;
return 3;
}
复制代码
执行generator将会返回一个遍历器对象,用于遍历generator内部的状态。
let g = gen();
g.next(); // { value: 1, done: false }
g.next(); // { value: 2, done: false }
g.next(); // { value: 3, done: true }
g.next(); // { value: undefined, done: true }
复制代码
能够看到,generator函数有一个最大的特色,能够在内部执行的过程当中交出程序的控制权,yield至关于起到了一个暂停的做用;而当必定的状况下,外部又将控制权再移交回来。 咱们用generator来封装代码,在异步任务处使用yield关键词,此时generator会将程序执行权交给其余代码,而在异步任务完成后,调用next方法来恢复yield下方代码的执行。以readFile为例,大体流程以下:
// 咱们的主任务——显示关键字
// 使用yield暂时中断下方代码执行
// yield后面为promise对象
const showKeyword = function* (filepath) {
console.log('开始读取');
let keyword = yield readFile(filepath);
console.log(`关键字为${filepath}`);
}
// generator的流程控制
let gen = showKeyword();
let res = gen.next();
res.value.then(res => gen.next(res));
复制代码
能够看到,上面的方法虽然都在必定程度上解决了异步编程中回调带来的问题。然而
所以,这里在介绍一个方法,它就是es7中的async/await。 简单介绍一下async/await。基本上,任何一个函数均可以成为async函数,如下都是合法的书写形式
async function foo () {};
const foo = async function () {};
const foo = async () => {};
复制代码
未完待续——