비동기 nodejs 모듈 내보내기


82

모듈 내보내기를 구성하는 가장 좋은 방법이 무엇인지 궁금합니다. 아래 예에서 "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 설정).


실제 사용 사례를 가지고 놀았고 nconf.file ()이 존재하지 않는 파일로 호출되면 nconf가 제대로로드되므로 지금은 해결책이 필요하지 않습니다. 그러나 여전히 접근 방식에 관심이 있습니다.
Brett

같은 질문이 있는데, 약속을 내보내고 require종속성을 비동기 적으로로드 할 수 있습니다. 바벨 포맷터로 가능하다고 생각합니다. 그러나 나는 이것들에 대한 좋은 해결책이라고 생각하지 않습니다. :(
Junle Li 2015

답변:


66

내보내기는 기능 외부에 있기 때문에 작동하지 않습니다. 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를 사용하여 해결할 수도 있습니다. 이 메서드는 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;
});

Promise 문서 참조


3
두 개의 별도 (메인) 파일이 foo가 준비되지 않은 상태에서이 함수를 호출하면 작동하지 않습니다. 그렇죠? 오직 자신의 콜백 중 하나가 발사 될 것이다, 그것을 호출하는 최신이었다 중 ..
laggingreflex

이 경우에는 그렇습니다. 우리는 콜백 스택을 관리하지 않기 때문입니다. 그러나 모든 콜백을 저장하는 배열을 사용하면 쉽게 해결할 수 있습니다.
Techniv

세부 정보 : ReferenceError : async is not defined
1nstinct

1
당신이 말하는 첫 번째 예에서 다른 블록의 본질은 무엇인가 (1) : 나는이 개 질문이 if(typeof foo != 'undefined'){ cb(foo); // If foo is already define, I don't wait. } else { callback = cb; }. (2) 그 블록 require은이 모듈에 대한 s가 (비동기 적 여정에서) 값을 산출 할 때까지 계속 호출 한다는 것을 의미합니까 ? 아니면 수명 동안 모듈에 단 하나의 콜백 만 주어질 것이라고 가정 cb합니까? 즉, 후속 호출이 인수를 생략 할 수 있습니까?
내가 답변 원하는

1
@IWantAnswers,이 예제에서 모듈은 foo값을 사용해야하는 다른 모듈에 의해 여러 시간이 필요할 수 있습니다 . 그러나 그것이 언제 일어 났는지 모릅니다. 따라서 초기이고 foo값이 아직 존재하지 않을 때 비동기 호출의 반환을 기다리기 위해 콜백을 저장합니다. 비동기 프로세스가 끝나면 저장된 모든 콜백이 스택 해제되고 배열이 더 이상 사용되지 않습니다. 이 시점에서 다른 모듈이이 모듈을 필요로하고 foo값 을 얻기 위해 구독하는 경우 값이 이미 설정되어 있으므로 스토어를 우회하여 콜백을 직접 실행합니다.
Techniv

16

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

나중에 await로 필요할 수 있습니다.

(async function(){

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

그것을 호출하는 것과 그렇지 않은 것의 차이점 / 함의를 설명 할 수 있습니까?
Bernardo Dal Corno

1
함수를 호출하지 않으면 실행하지 않고 함수를 내 보냅니다.
Jonas Wilms 19

14

약속을 사용하여 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) })

또는 Promise 자체를 직접 반환 할 수 있습니다.

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

1
이것은 ES6 및 Promises에 대한 정확하고 현대적인 답변입니다. 감사합니다.
Joshua Pinter 2017

1
질문 : Promise직접 대신 함수를 반환하는 이유가 있습니까? Promise직접 반환하면로 액세스 할 수 있습니다 asyncFunc.then(...). 꽤 새롭기 때문에 당신의 의견을 듣고 싶습니다.
Joshua Pinter 2017

1
그것도 작동합니다. 이 예제를 작성할 때 비동기 메서드를 사용하여 클래스를 내 보내서 함수처럼 공식화했다고 생각합니다. 하지만 다음과 같이 Promise를 내보낼 수 있습니다. const p = new Promise(...); export default p;그런 다음 가져 오기 모듈에서import p from '...'; p.then(...);
inostia

설명 해주셔서 감사합니다. 개인적 선호도라고 생각합니까 아니면 둘 중 하나를 사용하는 모범 사례가 있습니까?
Joshua Pinter 2017

비동기 모듈에 인수를 전달해야하는지 여부에 따라 달라지는 것 같습니다. 일반적으로 저에게 해당됩니다 (예 : id또는 다른 매개 변수). 첫 번째 예에서는 함수에서 const asyncFunc = (id) => ...사용할 수 있습니다 id. 당신은 그것을라고 부를 것 asyncFunc(id).then(...)입니다. 그러나 인수를 전달할 필요가 없다면 Promise를 직접 반환하는 것도 좋습니다.
inostia

11

또 다른 접근 방식은 변수를 개체 내부에 래핑하는 것입니다.

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

이니셜 라이저에 오류가있는 경우 적어도 콜백을 중단하는 대신 초기화되지 않은 값을 얻습니다.


3
모듈이 필요할 때 어떻게 "foo"를 얻습니까?
HelpMeStackOverflowMyOnlyHope

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

9

Promise를 사용할 수도 있습니다.

some-async-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

다른 모듈에서도 동일한 일이 발생할 수 있으며 예상대로 해결됩니다.

in-some-other-module.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 인스턴스).


0

다른 답변은 부분 답변으로 보였고 저에게 효과가 없었습니다. 이것은 다소 완전한 것 같습니다.

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

callback(null, wrapper.foo);대신 왜 가지고 callback(wrapper.foo);있습니까?
내가 답변 원하는

@IWantAnswers 첫 번째 인자는 에러이고, 두 번째는 결과
tsuz
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.