var vows = require('vows'),
assert = require('assert');
var doSomething = function(callback) {
callback(1);
}
vows.describe('this.callback test').addBatch({
'when callback occered': {
topic: function () {
doSomething(this.callback);
},
'should be successed': function(param) {
assert.equal(param, 1);
}
}
}).run();
테스트할 함수에 thsi.callback를 넘겨주고 콜백이 넘겨준 값을 테스트하는 간단한 spec입니다. 하지만 이렇게 작성을 하고 테스트를 실행하면 아래처럼 오류가 납니다.
처음에는 로직이 오류가 있는 것으로 생각했는데 로직은 이상이 없는데도(위 예제처럼) vow가 콜백을 받으면서 An unexpected error was caught가 발생했습니다. 이 문제로 꽤 고생을 했는데 문제는 상당히 간단한곳에 있었습니다. node.js에서는 관례적으로 콜백을 사용시 첫번째 파라미터로 err를 받도록 하고 있습니다.
function callback(err, result) {}
node.js 내장함수들을 포함해서 이런 형태의 콜백을 쓰는 것이 관례이고 Vows도 이 관례를 따르고 있기 때문에 콜백의 첫번째 파라미터가 null이 아닌 어떤 객체가 넘어오게 되면 해당 테스트를 진행하는데 오류가 발생했다고 생각하고 오류를 뿌려주게 됩니다.
var doSomething = function(callback) {
callback(null, 1); // <- fixed here
// or callback('', 1);
}
vows.describe('this.callback test').addBatch({
'when callback occered': {
topic: function () {
doSomething(this.callback);
},
'should be successed': function(err, param) { // <- fixed here
assert.equal(param, 1);
}
}
}).run();
위와 같이 node.js의 콜백 관례를 따라주면 정상적으로 테스트가 진행이 됩니다.(이런건 문서에 명시적으로 표시를 해줬으면 하네요 ㅠㅠ) 관례는 따르는게 좋다고 생각하지만 관례를 따르지 않고 콜백을 사용해서 테스트하고 싶다면 run() 메서드에 다음과 같이 오류를 체크하지 않도록 파라미터를 주면 됩니다.
vows.describe('').addBatch({}).run({error: false});
테스트 러너를 위해서 Suite를 외부로 내보내는 경우는 아래처럼 옵션을 줄 수 있습니다.
vows.describe('').addBatch({}).export(module, {error: false});
웃긴게 딱히 그런것도 아니고 어쩔때 null주면 에러나고 어떨땐 null안주면 에러나더군요..
node문제인지 vows의 문제인지 참 답답합니다
그런건 케이스바이 케이스로 봐야할것 같은데요 vows의 어떤 버그일수도 있구요. 전 mocha로 갈아타서 vows가 요즘 어떤지는 잘 모르겠지만 node쪽에 그런 버그가 있다는 얘긴 듣지 못했습니다.
안녕하세요.
혼자서 개발쪽 공부를 합니다.
자바스크립트 코어 좀 보다가 jQuery UI나 좀 만들어 본 기초자입니다.
node js를 공부하고 있는데요.
자바스크립트는 원래 하나의 스레드로 작동하는데, 비동기식으로 작동시키는 방법이 exec라고 하더라구요.
그래서 예제에 있는 것을 좀 뜯어 고쳐서 만들어 보았는데, 예상대로 작동이 안되서 질문드립니다.
start를 요청한 이후 바로 upload를 요청하면 start와 관계없이 upload가 실행되었으면 좋겠습니다.
A.
서버에 하나의 주소창에는
1.~~/start를 요청하고
다른 주소창에는
2. ~~/upload를 요청합니다.
B.
index쪽에서 요청을 받고 response를 해주기 위해서
requestHandler 쪽의 함수가 다음과 같습니다.
1번 주소를 받게되면 start함수가 실행되고
2번 주소를 받게 되면 upload함수가 실행되게 했습니다.
C.
그런데 원래는 하나의 thread로 javascript가 작동하니, while문이 도는 동안에는
두번째 upload가 실행이 되질 않습니다. 즉 upload는 whie문이 끝난 다음에 실행이 됩니다.
D.
그래서 비동기식 함수실행인 exec를 사용했습니다.
그런데 여전히 while문에 걸어준 1/9000sec 시간 이후에 upload가 실행합니다. 어떻게 해야할까요?
var exec = require("child_process").exec;
function start(response){
console.log("Request handler 'start' was called");
exec( "ls -lah",
{ timeout: 10000, maxBuffer: 20000*1024 },
function(error,stdout,stderr){
response.writeHead( 200, {"Content-Type": "text/plain"} );
response.write(stdout);
function sleep(milliSeconds) {
var startTime = new Date().getTime();
while (new Date().getTime() < startTime + milliSeconds);
response.write("exec print");
}
sleep(9000);
response.end();
});
}
function upload(response){
console.log("Request handler 'upload' was called");
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Hello Upload");
response.end();
}
exports.start = start;
exports.upload = upload;
말씀하신 것처럼 node.js는 싱글스레드이므로 한번에 하나의 작업만 실행하게 됩니다. 말슴하신 exec는 별도의 프로세스를 만들어서 실행하긴 하지만 이는 주신 소스에서 'ls' 명령어를 실행할 뿐이지 callback은 다시 메인 스레드로 돌아오므로 콜백은 메인스레드에서 동작하게 되므로 이 작업이 upload쪽 코드를 대기시키게 됩니다.
어떻게 테스트한지 모르겠지만 코드만 보았을 때는 start쪽에서 exec가 실행되고 그 사이에 upload 요청이 오면 upload가 실행된후 exec의 콜백이 실행되는 순서도 가능하지만 exec 실행완료후 upload 요청이 들어오면 while문 이후에 실행이 될 것 같네요.
process.nextTick으로 이런 작업을 나누어서 처리하는 방법도 있긴 합니다만 말씀하신 내용을 보면 아무때나 upload가 다른 작업을 인터럽트해서 뺏어오길 바라시는 것 같은데 그런 방법은 없습니다. 그런식으로 구현하시려면 upload 혹은 start를 다른 어플리케이션으로 띄우시고 요청을 그쪽으로 보내서 처리하도록 나누어서 처리해야 합니다.
말씀 고맙습니다.
제질문의 요는 start의 실행에 관계 없이 upload가 같은 시간에 구현이 되기를 바랐습니다.
그렇지만, 답변해 주신것 처럼.
그렇게 안된다면, 뭣하러 별도의 프로세스를 만드는 것이지요.
하나의 쓰레드를 마치 여러개처럼 만들어주는 메소드가 exec가 아닌거로군요.
그러니까, exec는 어느때 사용하는 것인지요?
추신 - 그럼 모듈을 두개를 두고 각각 따로 한번 실행해 보겠습니다. 그래도 안되면,질문한번 다시 들도 될까요?
exec의 실행자체는 별도로 되지만 주신 소스처럼 callback은 노드가 실행하는 것입니다. 작업속도가 exec가 크다면 별도로 비동기로 실행하는게 의미가 있겠지요.
네 제가 아는 부분이라면 질문하셔도 됩니다.