首先,函数必须是可被赋值的变量,这个时候,才能作为参数被传递. 其次,函数必须是闭包,才能实现异步回调. 为什么是闭包呢?因为在JavaScript中,函数就是闭包,而异步函数可以准确获取上下文中的变量. 要知道,异步函数是异步的,也就是说,当前js主任务已经全部执行完毕,一切变量都会被销毁的,而异步函数是因为闭包的关系,引用了外部变量造成,外部变量在js执行完毕后,变量因为引用次数不为零.不能被垃圾回收机制回收. 进而异步函数可以继续从上下文中准确的调用内存存址中的变量.
const add = (a,b) => a+b;
function middleware(call){
console.log('before');
call(3,4)
console.log('after');
}
middleware((a,b)=>{
console.log('result is ', add(a,b))
})
因为是同步的执行关系,执行顺序如下,
before
result is 7
after
下面请看异步的代码
const add = (a,b) => a+b;
function middleware(call){
console.log('before');
call(3,4)
console.log('after');
}
middleware((a,b)=>{
setTimeout(() => {
console.log('result is ', add(a,b))
}, 100);
})
异步代码执行结果如下
before
after
result is 7
原因是因为,当异步代码执行完毕之后,会把控制权交回给事件模型,因此,重新在任务队列中插入执行的代码,任务队列又在函数调用栈中插入待执行代码. JavaScript的灵活之处再次体现出来,闭包的原因,上下文被引用的变量并没有消失,因此,异步函数可以在任何地方,任何时间,调用函数.也不需要维护上下文中的变量.
下面的函数读取代码,之前读取过的代码会被缓存在内存中,,但是同时也会带来一个问题,那就是无法判断函数是同步执行还是异步. 因此约定俗称,要么全部同步代码,要么全部异步执行的代码,
const fs = require('fs');
const cache = {};
function inconsistenRead(file,call){
if(cache[file]){
call(cache[file]);
} else {
fs.readFile('index.html','utf-8',((err,data)=>{
cache[file] = data;
call(data);
}))
}
}
function createFileReader(file){
const listeners = [];
inconsistenRead(file,val=>{
listeners.forEach(listener=>listener(val))
});
return {
onDataReady: listener => listeners.push(listener)
}
}
const reader1 = createFileReader("index.html");
reader1.onDataReady(data=>{
console.log(data);
const reader2 = createFileReader('index.html');
reader2.onDataReady(data=>{
console.log('data');
})
})
建议在程序启动时选择同步阻塞代码,或者在不影响程序处理并发请求的时候选择阻塞代码,其他都选择异步代码.
const fs = require('fs');
const cache = {};
function inconsistenRead(file) {
if (cache[file]) {
return (cache[file])
} else {
cache[file] = fs.readFileSync(file, 'utf-8');
return cache[file];
}
};
function createFileReader(file) {
const listeners = [];
const res = inconsistenRead(file);
listeners.forEach(listener => listener(res));
return {
onDataReady: listener => {
listeners.push(listener);
console.log(listeners);
listener(res);
}
}
};
const reader1 = createFileReader("index.html");
reader1.onDataReady(data => {
console.log(data);
console.table('======================================');
const reader2 = createFileReader('index.html');
reader2.onDataReady(data => {
console.log(data);
})
});
将同步代码放在process.nextTick()
中执行,当然你也可以用setImmediate().也就是下一个事件循环周期,.修改inconsistenRead
代码如下:
const fs = require('fs');
const cache = {};
function inconsistenRead(file,call){
if(cache[file]){
process.nextTick(
()=>call(cache[file])
)
} else {
fs.readFile(file,'utf-8',((err,data)=>{
cache[file] = data;
call(data);
}))
}
}
function createFileReader(file){
const listeners = [];
inconsistenRead(file,val=>{
listeners.forEach(listener=>listener(val))
});
return {
onDataReady: listener => listeners.push(listener)
}
}
const reader1 = createFileReader("index.html");
reader1.onDataReady(data=>{
console.log(data);
console.table('======================================');
const reader2 = createFileReader('index.html');
reader2.onDataReady(data=>{
console.log(data);
})
})
触发于任何其他I/O事件被调用之前
触发于任何其他I/O事件被调用之后
下面查看文件读取函数的API,同样是基于这样的设计原则. 回调置尾是为了提升函数可读性. 错误暴露优先,保证每次回调都会检查,另一个是err始终为ERROR类型.
fs.readFile(fileName, [options], (err,data)=>{});
下面便是读取文件的代码,遵从将回调函数的错误作为第一个参数进行传递的原则,并且优先暴露错误. 存在2种错误, 1.读取文件错误 2.读取的文件不是正确的json格式,发生解析错误.
const fs = require('fs');
function readJSON(file, call){
fs.readFile(file,'utf-8',(err,data)=>{
let parsed;
if(err){
return call(err);
}
try {
parsed = JSON.parse(data);
} catch (err) {
return call(err);
};
call(null, parsed); // 遵从回调函数第一个参数为错误参数的原则.
})
}
readJSON('s.json',(err,data)=>{
if(err){
console.log('err:',err);
}
console.log(data);
})
console.log('ttt');
如果把try...catch代码删了会怎样.
const fs = require('fs');
function readJSON(file, call){
fs.readFile(file,'utf-8',(err,data)=>{
let parsed;
if(err){
return call(err);
}
// try {
// parsed = JSON.parse(data);
// } catch (err) {
// return call(err);
// };
call(null, parsed); // 遵从回调函数第一个参数为错误参数的原则.
})
}
readJSON('s.json',(err,data)=>{
if(err){
console.log('err:',err);
}
console.log(123);
console.log(data);
})
console.log('sdf');
于是乎,报错如下,并且代码阻塞了..
SyntaxError: Unexpected end of JSON input
at JSON.parse (<anonymous>)
at fs.readFile (/Users/pengliheng/www/adb/index.js:10:21)
at FSReqWrap.readFileAfterClose [as oncomplete] (internal/fs/read_file_context.js:53:3)
这是一段无法捕获的异步代码错误
try {
readJSON('s.json',(err,data)=>{
if(err){
console.log('err:',err);
}
console.log(data);
})
} catch (err) {
console.log(err);
}