2013-11-27 131 views
35

我想知道什麼是配置模塊導出的最佳方法。下面的「async.function」的例子可以是一個或FS HTTP請求,簡化爲舉例起見:異步nodejs模塊導出

這裏的示例代碼(asynmodule.js):

var foo = "bar" 
async.function(function(response) { 
    foo = "foobar"; 
    // module.exports = foo; // having the export here breaks the app: foo is always undefined. 
}); 

// having the export here results in working code, but without the variable being set. 
module.exports = foo; 

如何可以導出模塊只有一次異步回調已被執行?

編輯 我的實際使用情況的快速說明:我正在寫一個模塊用來配置fs.exists nconf中(https://github.com/flatiron/nconf)()回調(即它將解析一個配置文件,併成立nconf中)。

+0

一直在玩我的實際用例,並且nconf加載正常,如果nconf.file()被調用時不存在的文件,所以現在我不需要一個解決方案。但我仍然對這種方法感興趣。 – Brett

+0

我有同樣的問題,我想導出一個承諾,並且'require'異步加載依賴。我認爲這是可能與巴貝格式化。但是,我不認爲這些解決方案很好。 :( –

回答

33

您的導出無法正常工作,因爲它在foo聲明的內部時在功能之外。但是如果你把輸出放在裏面,當你使用你的模塊時,你不能確定輸出是被定義的。

使用ansync系統的最佳方式是使用回調。您需要導出回調分配方法以獲取回調,並在異步執行時調用它。

實施例:

var foo, callback; 
async.function(function(response) { 
    foo = "foobar"; 

    if(typeof callback == 'function'){ 
     callback(foo); 
    } 
}); 

module.exports = function(cb){ 
    if(typeof foo != 'undefined'){ 
     cb(foo); // If foo is already define, I don't wait. 
    } else { 
     callback = cb; 
    } 
} 

這裏async.function只是象徵一個異步調用的佔位符。

在主

var fooMod = require('./foo.js'); 
fooMod(function(foo){ 
    //Here code using foo; 
}); 

多回調方式

如果你的模塊需要不止一次你需要管理回調的陣列更被稱爲:

var foo, callbackList = []; 
async.function(function(response) { 
    foo = "foobar"; 

    // You can use all other form of array walk. 
    for(var i = 0; i < callbackList.length; i++){ 
     callbackList[i](foo) 
    } 
}); 

module.exports = function(cb){ 
    if(typeof foo != 'undefined'){ 
     cb(foo); // If foo is already define, I don't wait. 
    } else { 
     callback.push(cb); 
    } 
} 

這裏async.function只是一個佔位符來象徵一個異步調用。

在主

var fooMod = require('./foo.js'); 
fooMod(function(foo){ 
    //Here code using foo; 
}); 

無極方式

您還可以使用無極解決。這種方法支持多個呼叫由無極設計:

var foo, callback; 
module.exports = new Promise(function(resolve, reject){ 
    async.function(function(response) { 
     foo = "foobar" 

     resolve(foo); 
    }); 
}); 

這裏async.function只是象徵一個異步調用的佔位符。

在主

var fooMod = require('./foo.js').then(function(foo){ 
    //Here code using foo; 
}); 

參見Promise documentation

+0

啊,很有道理。謝謝Techniv。 – Brett

+3

如果兩個單獨的(主)文件調用這個函數而foo沒有準備好,這是行不通的吧?只有其中一個回調將被解僱,以最新的情況爲準。 – laggingreflex

+0

在這種情況下,是的。因爲我們不管理回調堆棧。但用數組來存儲所有的回調很容易解決。 – Techniv

8

另一種方法將被包裝的物體內部的變量。

var Wrapper = function(){ 
    this.foo = "bar"; 
    this.init(); 
}; 
Wrapper.prototype.init = function(){ 
    var wrapper = this; 
    async.function(function(response) { 
    wrapper.foo = "foobar"; 
    }); 
} 
module.exports = new Wrapper(); 

如果初始化程序有錯誤,至少仍然會得到未初始化的值而不是掛起回調。

+0

這是巧妙的謝謝 – abhilash

+3

當你需要模塊時,你如何得到「foo」? – HelpMeStackOverflowMyOnlyHope

+0

var wrapper = require('wrapper'); console.log(wrapper.foo) – vangoz

7

您還可以使用承諾的:

一些-異步module.js

module.exports = new Promise((resolve, reject) => { 
    setTimeout(resolve.bind(null, 'someValueToBeReturned'), 2000); 
}); 

main.js

var asyncModule = require('./some-async-module'); 

asyncModule.then(promisedResult => console.log(promisedResult)); 
// outputs 'someValueToBeReturned' after 2 seconds 

同樣可以發生在不同的模塊中L同時,解決預期:

在幾家,其他-module.js

var asyncModule = require('./some-async-module'); 

asyncModule.then(promisedResult => console.log(promisedResult)); 
// also outputs 'someValueToBeReturned' after 2 seconds 

注意,承諾對象被創建一次,然後它是由節點緩存。每個require('./some-async-module')將返回相同的對象實例(在這種情況下爲promise實例)。

0

其他答案似乎是部分答案,並沒有爲我工作。這似乎是有點完整:

一些-module.js

var Wrapper = function(){ 
    this.callbacks = []; 
    this.foo = null; 
    this.init(); 
}; 
Wrapper.prototype.init = function(){ 
    var wrapper = this; 
    async.function(function(response) { 
    wrapper.foo = "foobar"; 
    this.callbacks.forEach(function(callback){ 
     callback(null, wrapper.foo); 
    }); 
    }); 
} 
Wrapper.prototype.get = function(cb) { 
    if(typeof cb !== 'function') { 
     return this.connection; // this could be null so probably just throw 
    } 
    if(this.foo) { 
     return cb(null, this.foo); 
    } 
    this.callbacks.push(cb); 
} 
module.exports = new Wrapper(); 

main.js

var wrapper = require('./some-module'); 

wrapper.get(function(foo){ 
    // foo will always be defined 
}); 

main2.js

var wrapper = require('./some-module'); 

wrapper.get(function(foo){ 
    // foo will always be defined in another script 
}); 
6

ES6使用答案承諾:

const asyncFunc =() => { 
    return new Promise((resolve, reject) => { 
     // Where someAsyncFunction takes a callback, i.e. api call 
     someAsyncFunction(data => { 
      resolve(data) 
     }) 
    }) 
} 

export default asyncFunc 

... 
import asyncFunc from './asyncFunc' 
asyncFunc().then(data => { console.log(data) }) 

或者你可以返回無極本身直接:

const p = new Promise(...) 
export default p 
... 
import p from './asyncModule' 
p.then(...) 
+0

這是ES6和Promises的正確,現代的答案。這次真是萬分感謝。 –

+1

問題:你有沒有直接返回函數而不是'Promise'的原因?如果你直接返回'Promise',你可以用'asyncFunc.then(...)'訪問它,對嗎?很新,所以想得到您的意見。 –

+1

這也可以。我想當我寫這個例子的時候,我用一個異步方法導出了一個類,這個方法就像一個函數一樣。但是你可以像這樣輸出Promise:'const p = new Promise(...);導出默認p;'然後在你的導入模塊中'從'...'導入p; p.then(...);' – inostia

2

的ES7的做法將是一個立刻調用異步功能在module.exports:

module.exports = (async function(){ 
//some async initiallizers 
//e.g. await the db module that has the same structure like this 
    var db = await require("./db"); 
    var foo = "bar"; 

    //resolve the export promise 
    return { 
    foo 
    }; 
})() 

這可能需要等待以後:

(async function(){ 

    var foo = await require("./theuppercode"); 
    console.log(foo); 
})();