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');
}
}
}
報錯截圖
fs.readFile執行成功了,只是JSON.parse報錯
直接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.模塊化
模塊化的作用是
- 將所有功能函數與變量私有化,只通過暴露出一個接口供外界進行訪問。
- 爲了防止變量污染全局要使用模塊化。
模塊化是是實現過程以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屬性中指定的目錄/文件
未完待續