您現在的位置是:首頁 > 單機遊戲首頁單機遊戲
如何用Q模組的鏈式呼叫來解決nodejs中的回撥地獄問題
- 2021-08-29
udietoo怎麼修改地獄火炬
對於用過Nodejs的小夥伴都知道
Nodejs是典型的非同步開發模式, 它最大的優勢也是非同步機制
這種機制帶來巨大的效能優勢的同時, 也帶來了回撥模式引發的回撥地獄問題,從而導致開發者的夢魘
作為例子, 先準備3個檔案
先來一個最簡單的回撥樣例:
const fs = require(“fs”);fs。readFile(‘d:/aa。txt’, (err, res1)=>{ if(err) console。error(err); else{ console。log(‘1。。。’, res1。toString()); }});console。log(“2。。。這句是在fs。readFile順序後面的語句。”);
執行結果:
結果顯示“2。。。”比“1。。。”先輸出
原因是“1。。。”是在回撥後執行, 比順序執行的“2。。。”慢上半拍
現在提出問題: 要求依次輸出“aa。txt”, “bb。txt”, “cc。txt”的檔案內容, 要怎麼做?
如果按傳統的順序程式設計思路,將會這麼寫:
fs。readFile(‘d:/aa。txt’, (err, res1)=>{ if(err) console。error(err); else{ console。log(‘1。。。’, res1。toString()); }});fs。readFile(‘d:/aa。txt’, (err, res2) => { if (err) console。error(err); else { console。log(‘1。1。。。’, res2。toString()); }});fs。readFile(‘d:/cc。txt’, (err, res3) => { if (err) console。error(err); else { console。log(‘1。2。。。’, res3。toString()); }});console。log(“2。。。這句是在fs。readFile順序後面的語句。”);
這是成功了?
哦喲, 居然可以!
其實這是假象! 這個案例中, 3個檔案大小一致, 導致呼叫和回撥時機和時間幾乎相同, 導致看上去好像是“順序執行”成功, 但我們只要把其中一個換成讀取不同大小的檔案, 就能看出區別
比如把bb。txt換成一個稍大一些的HTML檔案, 由於讀取檔案大小不同, 所需的消耗時間也不同, 非同步就表現出不同的結果如下圖
很顯然,1。2跑到1。1前面來了
這時候怎麼解決這個問題?
最簡單的思路就是:那就在上一個回撥塊內執行下一個回撥塊:
fs。readFile(‘d:/aa。txt’, (err, res1)=>{ if(err) console。error(err); else{ console。log(‘1。。。’, res1。toString()); fs。readFile(‘d:/aa。html’, (err, res2) => { if (err) console。error(err); else { console。log(‘1。1。。。’, res2。toString()); fs。readFile(‘d:/cc。txt’, (err, res3) => { if (err) console。error(err); else { console。log(‘1。2。。。’, res3。toString()); } }); } }); }});console。log(“2。。。這句是在fs。readFile順序後面的語句。”);
這下可以了
應該看出來, 雖然這樣寫能解決問題
但是!
程式碼變得開始難以閱讀
試想一下, 如果這種調用出現10次\10次, 那這程式碼還能讀(維護)嗎? 這就是經典回撥地獄現象
解決辦法也有不少
我們本次先用Q模組來演示怎麼把這種程式碼變得稍微優雅一點
先安裝Q模組
npm install q
然後程式碼改進一下:
const Q = require(“q”);//封裝一下讀檔案的方法function rfile(fn){ let deferred = Q。defer(); fs。readFile(fn, (err, res) => { if (err){ deferred。reject(err); }else { deferred。resolve(res); } }); return deferred。promise;}
讀一個檔案的例子:
let res;rfile(‘d:/aa。txt’)。then((res1) => { res = res1。toString(); console。log(1。1, res);})。then(()=>{ console。log(1。2, res);})console。log(“2。。。這句是在fs。readFile順序後面的語句。”);
執行成功
多個檔案按順序怎麼讀呢
let res = [];rfile(‘d:/aa。txt’)。then((res1)=>{ res[0] = res1。toString(); console。log(1。1, res);})。then( rfile(‘d:/bb。txt’)。then((res2) => { res[1] = res2。toString(); console。log(1。2, res); })。then( rfile(‘d:/cc。txt’)。then((res3) => { res[2] = res3。toString(); console。log(1。3, res); })。then(()=>{ console。log(1。4, res); }) ))console。log(“2。。。這句是在fs。readFile順序後面的語句。”);
與預期結果相符
這種寫法, 與回撥地獄相比,有了很大的改善,多層執行只要同級多個then即可
好像叫瀑布流(序列)的方式?
缺點是要把非同步程式碼封裝在方法裡再呼叫
個人覺得, 這種方法還是不夠徹底
下期用co模組+Promise的方式記錄一篇, 實現真正的順序程式碼方式。
複雜的問題簡單化
每次只關注一個知識點
對技術有興趣的小夥伴可以關注我, 以後會經常分享各種奇奇怪怪又實用的技術知識