JS中因为闭包可以将上下文保存到内存中的特性,使异步中的回调机制在js中使用的更加方便。因此,当operation完成后,callback可以正确执行。异步函数中会使用回调函数,但是使用回调函数的不一定是异步函数。
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源码中的任务队列
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');
}
}
}
报错截图
上面例子不会触发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
模块化的作用是
模块化是是实现过程以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
}
require中模块查找机制
文件模块: /绝对路径寻找,./相对路径寻找
代码模块: 如果没有/或./则直接寻找node.js模块代码 包模块:
如果以上都没找到,则取寻找node_modele下的模块
寻找策略
未完待续