javascript - nodejs實現異步時遇到的一個問題
問題描述
例如有a,b,c三個函數,分別都執行同步操作,為了簡化我把同步操作簡化了一下
function c(m) { m = m + 1; return m;}function b(m) { m = m + 1; return c(m);}function a(){ let m = 0; return b(m);}
執行 a() 輸出的是2但是如果c函數執行的不是同步函數,是異步操作例如
function c(m) { setTimeout(function () {m = m + 1; }, 1000) return m;}
執行a()時,要想正確輸出2,就得把c通過promise或者async進行封裝,類似
function promiseC(m) { return new Promise((resolve, reject) => {setTimeout(function () {m = m + 1;resolve(m);}, 1000) }}async function c(m) { m = await promiseC(m); return m; }
因為c變為異步函數,b要調用c,b也要改為異步的,如此類推a也得改為異步
async function b(m) { m = m + 1; return await c(m);}async function a(){ let m = 0; return await b(m);}
a().then(function(data) {
console.log(data)
})這樣才能輸出2
為了正確輸出2,我把a,b都改變了,不知道有沒有其他方法可以避免改變a,b又能達到正確輸出呢?由于剛開始寫代碼時沒有考慮到異步的情況,像a,b這些函數都是分布到不同文件里面,而且數量比較多,現在為了讓c可以執行異步操作,改起來太難了,不知道大家有沒有其他好的方法?
下面是新添加的問題利用下面回答中直接返回promise對象的方法可以解決以上的問題,但是實際代碼更多的結構是這樣的
function c(m) { m = m + 1; return m;}function b(m) { m = m + 1; let n = c(m) n = n + 1 return n;}function a(){ let m = 0; let k = b(m); k = k + 1; return k;}
如果按這個方法,我得改造a,b的return方法才能讓a,b返回promise對象,對于這樣的結構不知道還有沒有不改動a,b函數實現正確輸出的方法
問題解答
回答1:很遺憾的告訴你,node這邊是顯式異步的,所以你把一個函數從同步改成異步,那么依賴它的函數也必須做更改,重構的時候確實是個頭疼的事情,你還是忍著改改吧。
像fibjs這種不需要異步關鍵字的重構起來就很省心了,你改了c不需要改動a和b,因為隱式異步不需要你指示它。
回答2:還是對Promise的理解不到位啊。這里沒必要改動b()和a()的。
對于函數c,只需要返回一個promise對象,經過函數b的時候,直接同步返回這個Promise對象,不需要改動函數b使其為異步函數,因為異步操作是在函數c中,b中只進行了同步操作。此時需要在函數a中捕獲這個Promise,所以代碼可以改成這樣
function promiseC(m) { return new Promise((resolve, reject) => {setTimeout(function () { m = m + 1; resolve(m);}, 1000) })}function c(m) { m = promiseC(m); return m;}function b(m) { m = m + 1; return c(m);}function a() { let m = 0; return b(m);}p.then(function(a){ console.log(a)})
所以,這里函數a(),b()如果不處理異步操作的返回值,那為何要把他改成Async函數呢。
回答3:可以試試 http://fibjs.org/docs/manual/... 直接轉成同步即可
回答4:不得不說我盯著屏幕打了好些草稿, 最終還是失敗了.
我想不出有什么方法能在 js 里阻塞當前函數但是又能及時執行 promise 的 resolve. 失敗的思路如下
c_result=nullc=async (m)=>{return m+1}c_sync = (m)=>{ let n=0pc=c(m).then((m)=>{c_result=m}) while(c_result===null && n++<100){} return c_result}b=(m)=>{return c_sync(m+1)}a=()=>{return b(0)}a()
它的問題在于, 雖然while(c_result===null && n++<100){}阻塞了函數c_sync, 但是也阻止了.then回調的執行. 由于單線程異步的機制, 當某一個回調觸發的時候, 如果線程正忙, 這個回調是沒法插隊的, 從而導致循環執行過程中, c_result沒辦法被變量 m 賦值.也就沒辦法退出循環.
但是我覺得這個問題很有意思. 找到了一篇相關文章. 作者通過一個外部二進制庫結局了局部阻塞的問題.
http://blog.csdn.net/xingqili...
我的理解是:基于 js 引擎自身的事件循環, 我們不能阻塞某個塊. 因為對于 js 代碼而言引擎的事件循環是在底層. 但是對于外部的二進制模塊而言. 其可以阻塞自身, 并保證 js 引擎的事件循環每一次都完全遍歷事件隊列----以保證自身阻塞期間在 js 引擎中新增的事件能被處理.
回答5:讓a()輸出promise,確實可以解決我提到的問題但是在真正修改代碼的時候,我發現大部分代碼的結構不是我上面問題這樣的而是下面新補充的結構
function c(m) { m = m + 1; return m;}function b(m) { m = m + 1; let n = c(m) n = n + 1 return n;}function a(){ let m = 0; let k = b(m); k = k + 1; return k;}回答6:
恕我直言,你沒有對 node.js 的事件循環機制和 event 核心模塊作深入的了解。promise 和 aysnc/await 確實是如今處理異步流程控制的主流,但并不是說沒有了它們就做不了了,這種簡單的問題回溯到 event 方式處理即可。
const EventEmitter = require(’events’);class MyEmitter extends EventEmitter {}const myEmitter = new MyEmitter();myEmitter.on(’a’, (m) => { console.log(’a -> b’); myEmitter.emit(’b’, m+1);});myEmitter.on(’b’, (m) => { console.log(’b -> c’); myEmitter.emit(’c’, m+1);});myEmitter.on(’c’, (m) => { console.log(’result’, m);});myEmitter.emit(’a’, 0);
相關文章:
1. html - 哪些情況下float會失效?2. mac連接阿里云docker集群,已經卡了2天了,求問?3. 就一臺服務器,mysql數據庫想實現自動備份,如何設計?4. css3 - text-overflow為何會在li的子標簽a下失效5. javascript - node.js不同模塊之間如何傳值6. android - 哪位大神知道java后臺的api接口的對象傳到前端后輸入日期報錯,是什么情況?求大神指點7. javascript - js控制元素樣式的疑惑8. [前端求職必看]前端開發面試題與答案精選_擴展問題9. docker Toolbox在win10 家庭版中打開報錯10. javascript - 求解答,koa-bodyparser獲取到的參數是空對象,為什么?????
