node.js设计模式第二章总结

1.js的回调函数

JS中因为闭包可以将上下文保存到内存中的特性,使异步中的回调机制在js中使用的更加方便。因此,当operation完成后,callback可以正确执行。异步函数中会使用回调函数,但是使用回调函数的不一定是异步函数。

2.JS的在异步与同步的回调机制

js中的异步函数使用回调机制可能会出现问题。
//异步

const cache = {};
const fs = require("fs");
function instanceReadSyn(filename,callback){
	if(cache[filename]){
		callback(cache[filename]);
	}else{
		fs.readFile(filename,'utf-8',(err,data)=>{
			if(err){
				callback(err);
			}else{
				cache[filename] = data;
				callback(data);
			}
		})
	}
}
function createFileReader(filename){
	const listeners = [];
	instanceReadSyn(filename,(data)=>{
		listeners.foreach((listener)=>{listener(data)});
	})
	return {
		addListener: (listener)=>{listeners.push(listener)}
	}
}
const file1 = createFileReader('lalala.txt');
//第一次打开文件为异步,所以会异步执行callback,因此callback会在
//file1.addListener执行之后触发callback回调。
file1.addListener((data)=>{
	console.log("第一次打开");
	//第二次打开文件,因为cache的存在,因此同步执行callback,但此时
	//file2.addListener没有执行,所以不会触发添加的监听函数。
	const file2 =  createFileReader("alala.txt');
	file2.addListener((data)=>{console.log("第二次打开");})
})
//第一次打开

造成上述问题的原因是因为,当不存在cache时,异步打开文件从而异步执行callback;当存在cache时,callback同步执行。

因为异步方法的问题,则改为同步方法

function instanceReadSyn(filename,callback){
	if(cache[filename]){
		return cache[filename];
	}else{
		cache[filename] = fs.readFileSync(filename,'utf-8');
		return cache[filename]}
}

改进异步方法

function instanceReadSyn(filename,callback){
	if(cache[filename]){
		//就算cache存在也会异步执行callback
		process.nextTick(()=>{
			callback(cache[filename]);
		})
	}else{
		fs.readFile(filename,'utf-8',(err,data)=>{
			if(err){
				callback(err);
			}else{
				cache[filename] = data;
				callback(data);
			}
		})
	}
}
//第一次打开
//第二次打开

SetInmmediate有同样的作用
Process.nextTick的执行回调是在I/O队列之前
SetInmmediate的执行回调是在I/O队列之后
扩展用法有vue源码中的任务队列

3.转换标准

1.callback一般位于输入参数中的最后一个
2.callback中的error参数一般位于第一个,data数据从第二个参数开始
3.error必须是Error类型的对象
出错代码必须得使用try catch,但是可能有时候出现try catch捕获不到的错误
以下代码当出现JSON.parse(data)中的data不是一个json

function instanceReadSyn(filename,callback){
    if(cache[filename]){
        process.nextTick(()=>{
            callback(cache[filename]);
        })
    }else{
    	//内部的fs.readFile执行成功了,所以try catch没有捕获到
        try{
            fs.readFile(filename,'utf-8',(err,data)=>{
                if(err){
                    callback(err);
                }else{
                    cache[filename] = data;
                    callback(null,JSON.parse(data));
                }
            })
        }catch{
            console.log('1');
        }
    }
}

报错截图
node.js设计模式第二章总结_第1张图片
上面例子不会触发try…catch,因为callback是异步回调进入事件轮询,fs.readFile执行成功,try…catch并没有捕获到异常,当callback执行时,try…catch早执行完了,所以捕获不到。
直接exit输出到控制台,这个时候可以使用process.on(‘uncaughtException’,(err)=>{console.log(err);process.exit(1)});推荐出现错误直接exit。
改进

try{
    callback(null,JSON.parse(data));
}catch(err){
    console.log(1);
}
//输出1

4.模块化

模块化的作用是

  1. 将所有功能函数与变量私有化,只通过暴露出一个接口供外界进行访问。
  2. 为了防止变量污染全局要使用模块化。

模块化是是实现过程以require为例:

const helper = require('aaa.js');

从上述代码可以看出require的内部实现,先执行aaa.js,然后将需要导出的方法以一个module.export对象的机制返回,module.export对象里面存放各种方法。

const loadModule = function(filename,module,reqquire){
	const wrapper = `function(filename,module,module.export){
		${fs.readFileSync(filename,'utf-8')}
		}(filename,module,require)`
		eval(wrapper);
	}
	const require = function (filenamet){
	const id = require.resolve(filename);
	const module = {
		id,
		exports: {}
	}
	loadmodule(filename,module,require);
	require.cache[id] = module;
	return module.exports;
}
loadmodule.cache = {};
require.resolve = (filename)=>{
	//返回唯一标识id
}
  • 模块化导出的方法变量在一个单独的作用域内部,相同的方法名与变量名可以定义到全局。
  • node.js中的exports仅仅是module.exports的一个副本,通过给exports添加属性是有用的,但是如果直接改变exports的引用内容将是无效的,因为没有改变module.exports的值。
  • 在使用require引用的模块一般不能引用异步方法,可能会出现错误。

require中模块查找机制

  • 文件模块: /绝对路径寻找,./相对路径寻找

  • 代码模块: 如果没有/或./则直接寻找node.js模块代码 包模块:

  • 如果以上都没找到,则取寻找node_modele下的模块

寻找策略

  • ${moduleName}.js
  • ${moduleName}/index.js
  • ${moduleName}/package.json的main属性中指定的目录/文件

未完待续

你可能感兴趣的:(js)