异步 nodejs 模块导出



我想知道配置模块导出的最佳方法是什么。以下示例中的"async.function"可以是 FS 或 HTTP 请求,为了示例而简化:

下面是示例代码(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(。

导出无法正常工作,因为它位于函数外部,而foo声明位于函数内部。但是,如果您将导出放在里面,则在使用模块时无法确定导出是否已定义。

使用异步系统的最佳方法是使用回调。您需要导出回调赋值方法来获取回调,并在异步执行时调用它。

例:

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;
});

承诺方式

你也可以使用承诺来解决这个问题。此方法支持多个调用由 Promise 的设计:

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;
});

请参阅承诺文档

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);
})();

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(...)

另一种方法是将变量包装在对象中。

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();

如果初始值设定项有错误,至少您仍然可以获得未初始化的值,而不是挂起回调。

您还可以使用承诺:

某个异步模块.js

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

主.js

var asyncModule = require('./some-async-module');
asyncModule.then(promisedResult => console.log(promisedResult)); 
// outputs 'someValueToBeReturned' after 2 seconds

同样的情况可能发生在不同的模块中,并且也会按预期解决:

在某个模块中.js

var asyncModule = require('./some-async-module');
asyncModule.then(promisedResult => console.log(promisedResult)); 
// also outputs 'someValueToBeReturned' after 2 seconds

请注意,promise 对象创建一次,然后由节点缓存。每个require('./some-async-module')将返回相同的对象实例(在本例中为 promise 实例(。

其他答案似乎是部分答案,对我不起作用。这似乎有些完整:

某模块.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();

主.js

var wrapper = require('./some-module');
wrapper.get(function(foo){
    // foo will always be defined
});

主2.js

var wrapper = require('./some-module');
wrapper.get(function(foo){
    // foo will always be defined in another script
});

最新更新