javascript - nodejs實作非同步時遇到的一個問題
黄舟
黄舟 2017-07-03 11:41:44
0
6
868

例如有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函數實現正確輸出的方法

黄舟
黄舟

人生最曼妙的风景,竟是内心的淡定与从容!

全部回覆(6)
阿神

很遺憾的告訴你,node這邊是顯式異步的,所以你把一個函數從同步改成異步,那麼依賴它的函數也必須做更改,重構的時候確實是個頭疼的事情,你還是忍著改改吧。

像fibjs這種不需要非同步關鍵字的重構起來就很省心了,你改了c不需要改動a和b,因為隱式非同步不需要你指示它。

洪涛

還是對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函數呢。

扔个三星炸死你

可以試試 http://fibjs.org/docs/manual/... 直接轉成同步即可

滿天的星座

不得不說我盯著螢幕打了好些草稿, 最終還是失敗了.

我想不出有什麼方法能在 js 裡阻塞當前函數但是又能及時執行 promise 的 resolve. 失敗的思路如下

c_result=null
c=async (m)=>{return m+1}
c_sync = (m)=>{
    let n=0
        pc=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++阻塞了函數c_sync, 但是也阻止了.then回調的執行. 由於單線程異步的機制, 當某一個回呼觸發的時候, 如果線程正忙, 這個回調是沒辦法插隊的, 從而導致循環執行過程中, c_result沒辦法被變量m 賦值.也就沒辦法退出循環.

但是我覺得這個問題很有意思. 找到了一篇相關文章. 作者透過一個外部二進位庫結局了局部阻塞的問題.

http://blog.csdn.net/xingqili...

我的理解是:
基於js 引擎自身的事件循環, 我們不能阻塞某個塊. 因為對於js 代碼而言引擎的事件循環是在底層. 但是對於外部的二進制模組而言. 其可以阻塞自身,並保證js 引擎的事件循環每一次都完全遍歷事件隊列----以保證自身阻塞期間在js 引擎中新增的事件能被處理.

巴扎黑

讓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;
}
世界只因有你

恕我直言,你沒有對 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);
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板