Callback hell回調地域 當我們以同步的方式編寫耗時的代碼,那麼就會阻塞JS的單線程,造成CPU一直等待IO完成才去執行後面的代碼。 而CPU的執行速度是遠遠大於硬碟IO速度的,這樣等待只會造成資源的浪費。 非同步IO就是為瞭解決這個問題的,非同步能儘可能不讓CPU閑著,它不會在那等著IO完成 ...
Callback hell回調地域
當我們以同步的方式編寫耗時的代碼,那麼就會阻塞JS的單線程,造成CPU一直等待IO完成才去執行後面的代碼。
而CPU的執行速度是遠遠大於硬碟IO速度的,這樣等待只會造成資源的浪費。
非同步IO就是為瞭解決這個問題的,非同步能儘可能不讓CPU閑著,它不會在那等著IO完成;而是傳遞給底層的事件迴圈一個函數,自己去執行下麵的代碼。等磁碟IO完成後,函數就會被執行來作為通知。
雖然非同步和回調的編程方式能充分利用CPU,但是當代碼邏輯變的越來越複雜後,新的問題出現了——Callback hell回調地域!
功能代碼
實現如下功能:
- 判斷一個文件是文件還是目錄;
- 如果是目錄,讀取這個目錄下的文件,找出結尾是txt的文件;
- 獲取這些txt文件大小。
非同步方式
function withoutPromise() {
let target = "test";
fs.stat(target, (err, stat)=>{
if(err){
throw err;
}
// 如果是文件夾
if(stat.isDirectory()){
fs.readdir(target, (err, files)=>{
// 遍歷files
files.forEach( f =>{
if(path.extname(f) === '.txt'){
fs.stat(path.join(target, f), (err, stat)=>{
console.log(f+ " : "+stat.size);
});
}
} );
});
}
});
}
Promise方式
async function withPromise() {
let target = "test";
//將fs.stat轉為一個可以返回Promise對象的方法
let pstat = util.promisify(fs.stat);
let stat = await pstat(target);
// 如果是文件夾
if(stat.isDirectory()){
//將fs.readdir轉為一個可以返回Promise對象的方法
let preaddir = util.promisify(fs.readdir)
let files = await preaddir(target)
files.forEach( async (f) => {
if(path.extname(f) === '.txt'){
let stat = await pstat(path.join(target, f));
console.log(stat.size);
}
});
}
}
Promise和async/await
Promise
和async/await
便是為瞭解決Callback hell的問題。
promise
promise
的作用是對非同步回調代碼包裝一下,把原來的一個回調函數拆成2個回調函數,這樣的好處是可讀性更好。語法如下:
// 創建promise對象
let promise = new Promise((resolve, reject)=>{
// 在非同步操作成功的情況選調用resolve,失敗的時候調用reject
fs.readFile('xxx.txt',(err, data)=>{
if(err){
reject(err)
}else {
resolve(data.toString())
}
})
});
// 使用promise
promise.then((text)=>{
//then方法是當Promise內部調用了resolve的時候執行
}).catch((err)=>{
//catch方法是當Promise內部調用了reject的時候執行
console.log(err);
})
- 語法註意:Promise內部的resolve和reject方法只能調用一次,調用了這個就不能再調用了那個;如果調用,則無效。
async/await
async/await
的作用是直接將Promise非同步代碼變為同步的寫法,註意,代碼仍然是非同步的。
語法要求:
await
只能用在async
修飾的方法中,但是有async
不要求一定有await
。await
後面只能跟async
方法和promise
。
假設擁有了一個promise對象,現在使用async/await可以這樣寫:
async function asyncDemo() {
try {
// 當promise的then方法執行的時候
let text = await promise
// 當你用promise包裝了所有的非同步回調代碼後,就可以一直await,真正意義實現了以同步的方式寫非同步代碼
console.log('非同步道明執行');
}catch (e){
// 捕獲到promise的catch方法的異常
console.log(e);
}
}
asyncDemo()
console.log('我是同步代碼');
非同步代碼的終極寫法
- 先使用
promise
包裝非同步回調代碼,可使用node提供的util.promisify
方法; - 使用
async/await
編寫非同步代碼。
參考
-
黑馬程式員 120天全棧區塊鏈開發 開源教程
https://github.com/itheima1/BlockChain
學習視頻
一番同步了對應的學習視頻到B站,長按識別可訪問一番B站主頁觀看。