programing

Node.js에서 비동기 함수의 긴 중첩을 방지하는 방법

linuxpc 2023. 8. 4. 22:43
반응형

Node.js에서 비동기 함수의 긴 중첩을 방지하는 방법

DB의 데이터를 표시하는 페이지를 만들고 싶어서 DB의 데이터를 가져오는 기능을 만들었습니다.저는 Node.js의 초보자일 뿐이므로, 제가 이해하기로는 모든 것을 하나의 페이지(HTTP 응답)에서 사용하려면 다음과 같이 모두 중첩해야 합니다.

http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/html'});
  var html = "<h1>Demo page</h1>";
  getSomeDate(client, function(someData) {
    html += "<p>"+ someData +"</p>";
    getSomeOtherDate(client, function(someOtherData) {
      html += "<p>"+ someOtherData +"</p>";
      getMoreData(client, function(moreData) {
        html += "<p>"+ moreData +"</p>";
        res.write(html);
        res.end();
      });
    });
  });

그런 함수가 많으면 내포가 문제가 됩니다.

이것을 피할 방법이 있습니까?여러 비동기 기능을 결합하는 방법과 관련이 있다고 생각합니다. 기본적인 것으로 보입니다.

흥미로운 관찰입니다.JavaScript에서는 일반적으로 인라인 익명 콜백 함수를 명명된 함수 변수로 대체할 수 있습니다.

다음 항목:

http.createServer(function (req, res) {
   // inline callback function ...

   getSomeData(client, function (someData) {
      // another inline callback function ...

      getMoreData(client, function(moreData) {
         // one more inline callback function ...
      });
   });

   // etc ...
});

다음과 같이 다시 작성할 수 있습니다.

var moreDataParser = function (moreData) {
   // date parsing logic
};

var someDataParser = function (someData) {
   // some data parsing logic

   getMoreData(client, moreDataParser);
};

var createServerCallback = function (req, res) {
   // create server logic

   getSomeData(client, someDataParser);

   // etc ...
};

http.createServer(createServerCallback);

그러나 다른 위치에서 논리를 콜백하기 위해 재사용할 계획이 없는 경우, 예에서와 같이 인라인 익명 함수를 읽는 것이 훨씬 더 쉽습니다.또한 모든 콜백의 이름을 찾을 필요가 없습니다.

또한 @pst가 아래 주석에서 언급한 바와 같이 내부 함수 내에서 폐쇄 변수에 액세스하는 경우 위의 내용은 간단한 변환이 아닙니다.이러한 경우 인라인 익명 기능을 사용하는 것이 훨씬 더 좋습니다.

케이, 이 모듈들 중 하나만 사용하면 됩니다.

다음과 같이 됩니다.

dbGet('userIdOf:bobvance', function(userId) {
    dbSet('user:' + userId + ':email', 'bobvance@potato.egg', function() {
        dbSet('user:' + userId + ':firstName', 'Bob', function() {
            dbSet('user:' + userId + ':lastName', 'Vance', function() {
                okWeAreDone();
            });
        });
    });
});

대상:

flow.exec(
    function() {
        dbGet('userIdOf:bobvance', this);

    },function(userId) {
        dbSet('user:' + userId + ':email', 'bobvance@potato.egg', this.MULTI());
        dbSet('user:' + userId + ':firstName', 'Bob', this.MULTI());
        dbSet('user:' + userId + ':lastName', 'Vance', this.MULTI());

    },function() {
        okWeAreDone()
    }
);

저는 대부분 다니엘 바살로의 의견에 동의합니다.복잡하고 깊이 중첩된 함수를 별도의 명명된 함수로 분할할 수 있는 경우 일반적으로 이 방법이 좋습니다.단일 함수 내에서 수행하는 것이 합리적인 경우 사용 가능한 많은 node.js 비동기 라이브러리 중 하나를 사용할 수 있습니다.사람들은 이 문제를 해결하기 위한 다양한 방법을 생각해냈습니다. 따라서 node.js 모듈 페이지를 보고 어떻게 생각하는지 확인하십시오.

저는 이를 위해 async.js라는 모듈을 직접 작성했습니다.이를 통해 위의 예를 다음과 같이 업데이트할 수 있습니다.

http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/html'});
  async.series({
    someData: async.apply(getSomeDate, client),
    someOtherData: async.apply(getSomeOtherDate, client),
    moreData: async.apply(getMoreData, client)
  },
  function (err, results) {
    var html = "<h1>Demo page</h1>";
    html += "<p>" + results.someData + "</p>";
    html += "<p>" + results.someOtherData + "</p>";
    html += "<p>" + results.moreData + "</p>";
    res.write(html);
    res.end();
  });
});

이 접근 방식의 한 가지 좋은 점은 '시리즈' 기능을 '병렬'로 변경하여 코드를 빠르게 변경하여 데이터를 병렬로 가져올 수 있다는 것입니다.또한 async.js는 브라우저 내부에서도 작동하므로 까다로운 비동기 코드가 발생할 경우 node.js와 동일한 방법을 사용할 수 있습니다.

그것이 유용하기를 바랍니다!

중첩 함수나 모듈이 아닌 배열에 이 방법을 사용할 수 있습니다.

눈에 훨씬 더 쉽습니다.

var fs = require("fs");
var chain = [
    function() { 
        console.log("step1");
        fs.stat("f1.js",chain.shift());
    },
    function(err, stats) {
        console.log("step2");
        fs.stat("f2.js",chain.shift());
    },
    function(err, stats) {
        console.log("step3");
        fs.stat("f2.js",chain.shift());
    },
    function(err, stats) {
        console.log("step4");
        fs.stat("f2.js",chain.shift());
    },
    function(err, stats) {
        console.log("step5");
        fs.stat("f2.js",chain.shift());
    },
    function(err, stats) {
        console.log("done");
    },
];
chain.shift()();

병렬 프로세스 또는 병렬 프로세스 체인에 대한 관용구를 확장할 수 있습니다.

var fs = require("fs");
var fork1 = 2, fork2 = 2, chain = [
    function() { 
        console.log("step1");
        fs.stat("f1.js",chain.shift());
    },
    function(err, stats) {
        console.log("step2");
        var next = chain.shift();
        fs.stat("f2a.js",next);
        fs.stat("f2b.js",next);
    },
    function(err, stats) {
        if ( --fork1 )
            return;
        console.log("step3");
        var next = chain.shift();

        var chain1 = [
            function() { 
                console.log("step4aa");
                fs.stat("f1.js",chain1.shift());
            },
            function(err, stats) { 
                console.log("step4ab");
                fs.stat("f1ab.js",next);
            },
        ];
        chain1.shift()();

        var chain2 = [
            function() { 
                console.log("step4ba");
                fs.stat("f1.js",chain2.shift());
            },
            function(err, stats) { 
                console.log("step4bb");
                fs.stat("f1ab.js",next);
            },
        ];
        chain2.shift()();
    },
    function(err, stats) {
        if ( --fork2 )
            return;
        console.log("done");
    },
];
chain.shift()();

저는 이런 목적으로 async.js를 많이 좋아합니다.

이 문제는 waterfall 명령으로 해결됩니다.

폭포(폭포, [콜백])

함수 배열을 직렬로 실행하고 각 함수의 결과를 배열의 다음 결과로 전달합니다.그러나 함수 중 하나가 콜백에 오류를 전달하면 다음 함수가 실행되지 않고 오류와 함께 주 콜백이 즉시 호출됩니다.

논쟁들

tasks - 실행할 함수 배열로, 각 함수는 완료 시 호출해야 하는 콜백(err, result1, result2, ...)을 전달합니다.첫 번째 인수는 오류이며(null일 수 있음) 추가 인수는 다음 작업으로 전달됩니다. callback(err, [results]) - 모든 함수가 완료되면 실행할 선택적 콜백입니다.마지막 작업의 콜백 결과가 전달됩니다.

async.waterfall([
    function(callback){
        callback(null, 'one', 'two');
    },
    function(arg1, arg2, callback){
        callback(null, 'three');
    },
    function(arg1, callback){
        // arg1 now equals 'three'
        callback(null, 'done');
    }
], function (err, result) {
    // result now equals 'done'    
});

req,res 변수의 경우 전체 비동기를 포함하는 함수(req,res){}와 동일한 범위 내에서 공유됩니다.폭포의 부름

뿐만 아니라 비동기식은 매우 깨끗합니다.제 말은 제가 이런 경우를 많이 바꾼다는 것입니다.

function(o,cb){
    function2(o,function(err, resp){
        cb(err,resp);
    })
}

처음 받는 사람:

function(o,cb){
    function2(o,cb);
}

다음은 다음과 같습니다.

function2(o,cb);

다음은 다음과 같습니다.

async.waterfall([function2,function3,function4],optionalcb)

또한 util.js에서 비동기를 위해 준비된 많은 사전 제작 함수를 매우 빠르게 호출할 수 있습니다.당신이 하고 싶은 일을 사슬로 묶고, o,cb가 보편적으로 취급되는지 확인하세요.이렇게 하면 전체 코딩 프로세스의 속도가 크게 향상됩니다.

당신에게 필요한 것은 약간의 통사적인 설탕입니다.이것을 확인:

http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/html'});
  var html = ["<h1>Demo page</h1>"];
  var pushHTML = html.push.bind(html);

  Queue.push( getSomeData.partial(client, pushHTML) );
  Queue.push( getSomeOtherData.partial(client, pushHTML) );
  Queue.push( getMoreData.partial(client, pushHTML) );
  Queue.push( function() {
    res.write(html.join(''));
    res.end();
  });
  Queue.execute();
}); 

깔끔하네요, 그렇죠?html이 배열이 된 것을 알 수 있습니다.이는 부분적으로 문자열이 불변하기 때문에 더 크고 큰 문자열을 삭제하는 것보다 배열에서 출력을 버퍼링하는 것이 더 좋습니다.다른 이유는 다음과 같은 또 다른 멋진 구문 때문입니다.bind.

Queue이 예시는 정말로 단지 예시일 뿐입니다.partial과 같이 할 수 있습니다.

// Functional programming for the rescue
Function.prototype.partial = function() {
  var fun = this,
      preArgs = Array.prototype.slice.call(arguments);
  return function() {
    fun.apply(null, preArgs.concat.apply(preArgs, arguments));
  };
};

Queue = [];
Queue.execute = function () {
  if (Queue.length) {
    Queue.shift()(Queue.execute);
  }
};

Async.js를 발견한 이후로 계속 사랑하고 있습니다.긴 중첩을 방지하기 위해 사용할 수 있는 기능이 있습니다.

설명서:-


시리즈(클릭, [콜백])

일련의 함수를 직렬로 실행합니다. 각 함수는 이전 함수가 완료되면 실행됩니다. [...]

논쟁들

tasks실행할 함수 배열로, 각 함수는 완료 시 호출해야 하는 콜백을 전달합니다.callback(err, [results])모든 기능이 완료되면 실행할 옵션 콜백입니다.이 함수는 배열에 사용된 콜백에 전달된 모든 인수의 배열을 가져옵니다.


예제 코드에 적용할 수 있는 방법은 다음과 같습니다.

http.createServer(function (req, res) {

    res.writeHead(200, {'Content-Type': 'text/html'});

    var html = "<h1>Demo page</h1>";

    async.series([
        function (callback) {
            getSomeData(client, function (someData) { 
                html += "<p>"+ someData +"</p>";

                callback();
            });
        },

        function (callback) {
            getSomeOtherData(client, function (someOtherData) { 
                html += "<p>"+ someOtherData +"</p>";

                callback(); 
            });
        },

        funciton (callback) {
            getMoreData(client, function (moreData) {
                html += "<p>"+ moreData +"</p>";

                callback();
            });
        }
    ], function () {
        res.write(html);
        res.end();
    });
});

제가 본 가장 간단한 구문 설탕은 노드 약속입니다.

npm install node-module || git clone https://github.com/kriszyp/node-promise

이를 사용하여 비동기 메서드를 다음과 같이 체인할 수 있습니다.

firstMethod().then(secondMethod).then(thirdMethod);

각 반환 값은 다음에서 인수로 사용할 수 있습니다.

여기서 수행한 작업은 비동기 패턴을 사용하여 순차적으로 호출되는 3개의 기능에 적용하는 것입니다. 각 기능은 시작하기 전에 이전 기능이 완료될 때까지 대기합니다. 즉, 동기화를 수행했습니다.비동기 프로그래밍의 요점은 여러 기능을 한 번에 모두 실행할 수 있고 각 기능이 완료될 때까지 기다릴 필요가 없다는 것입니다.

만약 getSomeDate()가 getSomeOtherDate()에 대해 아무것도 제공하지 않는 경우, 이것은 MoreData()를 얻기 위한 어떤 것도 제공하지 않습니다. 그렇다면 js가 허용하는 대로 비동기식으로 호출하거나, 상호의존적이거나(비동기식이 아닌) 단일 함수로 작성하는 것은 어떻습니까?

흐름을 제어하기 위해 중첩을 사용할 필요는 없습니다. 예를 들어, 세 개의 함수가 모두 완료된 후 응답을 전송하는 공통 함수를 호출하여 각 함수를 완료시킵니다.

이렇게 할 수 있다고 가정해 보겠습니다.

http.createServer(function (req, res) {
    res.writeHead(200, {'Content-Type': 'text/html'});
    var html = "<h1>Demo page</h1>";
    chain([
        function (next) {
            getSomeDate(client, next);
        },
        function (next, someData) {
            html += "<p>"+ someData +"</p>";
            getSomeOtherDate(client, next);
        },
        function (next, someOtherData) {
            html += "<p>"+ someOtherData +"</p>";
            getMoreData(client, next);
        },
        function (next, moreData) {
            html += "<p>"+ moreData +"</p>";
            res.write(html);
            res.end();
        }
    ]);
});

체인()만 구현하면 각 함수를 다음 함수에 부분적으로 적용하고 첫 번째 함수만 즉시 호출할 수 있습니다.

function chain(fs) {
    var f = function () {};
    for (var i = fs.length - 1; i >= 0; i--) {
        f = fs[i].partial(f);
    }
    f();
}

callback hell은 폐쇄와 함께 순수한 자바스크립트에서 쉽게 피할 수 있습니다.아래 솔루션은 모든 콜백이 함수(오류, 데이터) 서명을 따른다고 가정합니다.

http.createServer(function (req, res) {
  var modeNext, onNext;

  // closure variable to keep track of next-callback-state
  modeNext = 0;

  // next-callback-handler
  onNext = function (error, data) {
    if (error) {
      modeNext = Infinity;
    } else {
      modeNext += 1;
    }
    switch (modeNext) {

    case 0:
      res.writeHead(200, {'Content-Type': 'text/html'});
      var html = "<h1>Demo page</h1>";
      getSomeDate(client, onNext);
      break;

    // handle someData
    case 1:
        html += "<p>"+ data +"</p>";
        getSomeOtherDate(client, onNext);
        break;

    // handle someOtherData
    case 2:
      html += "<p>"+ data +"</p>";
      getMoreData(client, onNext);
      break;

    // handle moreData
    case 3:
      html += "<p>"+ data +"</p>";
      res.write(html);
      res.end();
      break;

    // general catch-all error-handler
    default:
      res.statusCode = 500;
      res.end(error.message + '\n' + error.stack);
    }
  };
  onNext();
});

저는 최근에 기다림이라는 더 간단한 추상화를 만들었습니다.동기화 모드(섬유 기준)에서 비동기 함수를 호출합니다.아직 초기 단계지만 효과가 있습니다.위치:

https://github.com/luciotato/waitfor

대기를 사용합니다.의 경우 모든 표준 nodejs 비동기 함수를 동기 함수인 것처럼 호출할 수 있습니다.

대기 시간을 사용합니다.코드는 다음과 같습니다.

var http=require('http');
var wait=require('wait.for');

http.createServer(function(req, res) {
  wait.launchFiber(handleRequest,req, res); //run in a Fiber, keep node spinning
}).listen(8080);


//in a fiber
function handleRequest(req, res) {
  res.writeHead(200, {'Content-Type': 'text/html'});
  var html = "<h1>Demo page</h1>";
  var someData = wait.for(getSomeDate,client);
  html += "<p>"+ someData +"</p>";
  var someOtherData = wait.for(getSomeOtherDate,client);
  html += "<p>"+ someOtherData +"</p>";
  var moreData = wait.for(getMoreData,client);
  html += "<p>"+ moreData +"</p>";
  res.write(html);
  res.end();
};

...또는 장황하게 설명하지 않으려는 경우(및 오류 파악 기능도 추가

//in a fiber
function handleRequest(req, res) {
  try {
    res.writeHead(200, {'Content-Type': 'text/html'});
    res.write(
    "<h1>Demo page</h1>" 
    + "<p>"+ wait.for(getSomeDate,client) +"</p>"
    + "<p>"+ wait.for(getSomeOtherDate,client) +"</p>"
    + "<p>"+ wait.for(getMoreData,client) +"</p>"
    );
    res.end();
  }
  catch(err) {
   res.end('error '+e.message); 
  }

};

모든 경우 getSomeDate, getSomeOtherDategetMoreData는 표준 비동기 함수여야 하며 마지막 매개 변수는 함수 콜백(err,data)이어야 합니다.

다음과 같이:

function getMoreData(client, callback){
  db.execute('select moredata from thedata where client_id=?',[client.id],
       ,function(err,data){
          if (err) callback(err);
          callback (null,data);
        });
}

이 문제를 해결하기 위해 저는 당신의 JS를 눈에 보이지 않게 미리 인식하는 nodent(https://npmjs.org/package/nodent) 를 작성했습니다.예제 코드는 (비동기화, 정말 - 문서 읽기)가 될 것입니다.

http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/html'});
  var html = "<h1>Demo page</h1>";
  someData <<= getSomeDate(client) ;

  html += "<p>"+ someData +"</p>";
  someOtherData <<= getSomeOtherDate(client) ;

  html += "<p>"+ someOtherData +"</p>";
  moreData <<= getMoreData(client) ;

  html += "<p>"+ moreData +"</p>";
  res.write(html);
  res.end();
});

분명히 다른 솔루션도 많지만 사전 처리는 런타임 오버헤드가 거의 없거나 전혀 없다는 장점이 있으며 소스 맵 지원 덕분에 디버깅도 쉽습니다.

저도 같은 문제가 있었습니다.나는 노드에 대한 주요 libs가 비동기 함수를 실행하는 것을 보았고, 코드를 구축하기 위해 매우 자연스럽지 않은 체인(세 개 이상의 메소드 conf 등을 사용해야 함)을 제시합니다.

저는 간단하고 읽기 쉬운 해결책을 개발하는 데 몇 주를 보냈습니다.EnqJS에 한 번 도전해 보세요.모든 의견을 주시면 감사하겠습니다.

다음 대신:

http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/html'});
  var html = "<h1>Demo page</h1>";
  getSomeDate(client, function(someData) {
    html += "<p>"+ someData +"</p>";
    getSomeOtherDate(client, function(someOtherData) {
      html += "<p>"+ someOtherData +"</p>";
      getMoreData(client, function(moreData) {
        html += "<p>"+ moreData +"</p>";
        res.write(html);
        res.end();
      });
    });
  });

Enq와 함께JS:

http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/html'});
  var html = "<h1>Demo page</h1>";

  enq(function(){
    var self=this;
    getSomeDate(client, function(someData){
      html += "<p>"+ someData +"</p>";
      self.return();
    })
  })(function(){
    var self=this;
    getSomeOtherDate(client, function(someOtherData){ 
      html += "<p>"+ someOtherData +"</p>";
      self.return();
    })
  })(function(){
    var self=this;
    getMoreData(client, function(moreData) {
      html += "<p>"+ moreData +"</p>";
      self.return();
      res.write(html);
      res.end();
    });
  });
});

코드가 이전보다 더 큰 것으로 나타나는지 관찰합니다.그러나 이전처럼 중첩되지 않았습니다.보다 자연스럽게 보이기 위해 체인을 즉시:

enq(fn1)(fn2)(fn3)(fn4)(fn4)(...)

그리고 그것이 돌아왔다고 말하면, 우리는 함수 안에서 다음과 같이 부릅니다.

this.return(response)

저는 꽤 원시적이지만 효과적인 방법으로 그것을 합니다.예를 들어 부모 및 자녀와 함께 모델을 구해야 하는데, 그들을 위해 별도의 쿼리를 해야 한다고 가정해 보겠습니다.

var getWithParents = function(id, next) {
  var getChildren = function(model, next) {
        /*... code ... */
        return next.pop()(model, next);
      },
      getParents = function(model, next) {
        /*... code ... */
        return next.pop()(model, next);
      }
      getModel = function(id, next) {
        /*... code ... */
        if (model) {
          // return next callbacl
          return next.pop()(model, next);
        } else {
          // return last callback
          return next.shift()(null, next);
        }
      }

  return getModel(id, [getParents, getChildren, next]);
}

Fibers https://github.com/laverdet/node-fibers 를 사용하면 비동기 코드가 (차단하지 않고) 동기화된 것처럼 보입니다.

저는 개인적으로 이 작은 포장지를 사용합니다. http://alexeypetrushin.github.com/synchronize 프로젝트의 코드 샘플(모든 방법은 실제로 비동기 파일 IO로 작동함) 콜백 또는 비동기 제어 흐름 도우미 라이브러리가 얼마나 엉망인지 상상조차 할 수 있습니다.

_update: (version, changesBasePath, changes, oldSite) ->
  @log 'updating...'
  @_updateIndex version, changes
  @_updateFiles version, changesBasePath, changes
  @_updateFilesIndexes version, changes
  configChanged = @_updateConfig version, changes
  @_updateModules version, changes, oldSite, configChanged
  @_saveIndex version
  @log "updated to #{version} version"

Task.js는 다음을 제공합니다.

spawn(function*() {
    try {
        var [foo, bar] = yield join(read("foo.json"),
                                    read("bar.json")).timeout(1000);
        render(foo);
        render(bar);
    } catch (e) {
        console.log("read failed: " + e);
    }
});

이 대신:

var foo, bar;
var tid = setTimeout(function() { failure(new Error("timed out")) }, 1000);

var xhr1 = makeXHR("foo.json",
                   function(txt) { foo = txt; success() },
                   function(err) { failure() });
var xhr2 = makeXHR("bar.json",
                   function(txt) { bar = txt; success() },
                   function(e) { failure(e) });

function success() {
    if (typeof foo === "string" && typeof bar === "string") {
        cancelTimeout(tid);
        xhr1 = xhr2 = null;
        render(foo);
        render(bar);
    }
}

function failure(e) {
    xhr1 && xhr1.abort();
    xhr1 = null;
    xhr2 && xhr2.abort();
    xhr2 = null;
    console.log("read failed: " + e);
}

다른 사람들이 응답한 후, 당신은 당신의 문제가 지역 변수라고 말했습니다.이렇게 하는 것은 하나의 외부 함수를 작성하여 로컬 변수를 포함한 다음 명명된 내부 함수를 사용하여 이름으로 액세스하는 것이 쉬운 방법인 것 같습니다.이렇게 하면 연결해야 하는 기능의 수에 관계없이 두 개의 깊은 곳에만 둥지를 틀 수 있습니다.

여기 제 신입이 사용하려는 시도가 있습니다.mysql중첩이 있는 Node.js 모듈:

function with_connection(sql, bindings, cb) {
    pool.getConnection(function(err, conn) {
        if (err) {
            console.log("Error in with_connection (getConnection): " + JSON.stringify(err));
            cb(true);
            return;
        }
        conn.query(sql, bindings, function(err, results) {
            if (err) {
                console.log("Error in with_connection (query): " + JSON.stringify(err));
                cb(true);
                return;
            }
            console.log("with_connection results: " + JSON.stringify(results));
            cb(false, results);
        });
    });
}

다음은 명명된 내부 함수를 사용한 다시 쓰기입니다.외부 함수with_connection로컬 변수의 홀더로도 사용할 수 있습니다. (여기 매개 변수가 있습니다.)sql,bindings,cb유사한 방식으로 작용하지만, 당신은 단지 몇 가지 추가적인 지역 변수를 정의할 수 있습니다.with_connection.)

function with_connection(sql, bindings, cb) {

    function getConnectionCb(err, conn) {
        if (err) {
            console.log("Error in with_connection/getConnectionCb: " + JSON.stringify(err));
            cb(true);
            return;
        }
        conn.query(sql, bindings, queryCb);
    }

    function queryCb(err, results) {
        if (err) {
            console.log("Error in with_connection/queryCb: " + JSON.stringify(err));
            cb(true);
            return;
        }
        cb(false, results);
    }

    pool.getConnection(getConnectionCb);
}

저는 아마도 인스턴스 변수로 객체를 만들고 이러한 인스턴스 변수를 로컬 변수의 대체물로 사용하는 것이 가능할 것이라고 생각해왔습니다.그러나 이제는 중첩 함수와 로컬 변수를 사용하는 위의 접근 방식이 더 간단하고 이해하기 쉽다는 것을 알게 되었습니다.OO를 풀려면 시간이 좀 걸리는 것 같아요, :-)

이것은 개체와 인스턴스 변수가 있는 이전 버전입니다.

function DbConnection(sql, bindings, cb) {
    this.sql = sql;
    this.bindings = bindings;
    this.cb = cb;
}
DbConnection.prototype.getConnection = function(err, conn) {
    var self = this;
    if (err) {
        console.log("Error in DbConnection.getConnection: " + JSON.stringify(err));
        this.cb(true);
        return;
    }
    conn.query(this.sql, this.bindings, function(err, results) { self.query(err, results); });
}
DbConnection.prototype.query = function(err, results) {
    var self = this;
    if (err) {
        console.log("Error in DbConnection.query: " + JSON.stringify(err));
        self.cb(true);
        return;
    }
    console.log("DbConnection results: " + JSON.stringify(results));
    self.cb(false, results);
}

function with_connection(sql, bindings, cb) {
    var dbc = new DbConnection(sql, bindings, cb);
    pool.getConnection(function (err, conn) { dbc.getConnection(err, conn); });
}

알고 보니bind어느 정도 도움이 될 수 있습니다.그것은 제가 만든 다소 추악한 익명 함수들을 제거할 수 있게 해줍니다. 그것은 메소드 호출에 자신을 전달하는 것을 제외하고는 아무 것도 하지 않았습니다.그 방법은 잘못된 값과 관련되어 있기 때문에 직접 전달할 수 없었습니다.this하지만 함께bind값을 지정할 수 있습니다.this내가 원하는 것.

function DbConnection(sql, bindings, cb) {
    this.sql = sql;
    this.bindings = bindings;
    this.cb = cb;
}
DbConnection.prototype.getConnection = function(err, conn) {
    var f = this.query.bind(this);
    if (err) {
        console.log("Error in DbConnection.getConnection: " + JSON.stringify(err));
        this.cb(true);
        return;
    }
    conn.query(this.sql, this.bindings, f);
}
DbConnection.prototype.query = function(err, results) {
    if (err) {
        console.log("Error in DbConnection.query: " + JSON.stringify(err));
        this.cb(true);
        return;
    }
    console.log("DbConnection results: " + JSON.stringify(results));
    this.cb(false, results);
}

// Get a connection from the pool, execute `sql` in it
// with the given `bindings`.  Invoke `cb(true)` on error,
// invoke `cb(false, results)` on success.  Here,
// `results` is an array of results from the query.
function with_connection(sql, bindings, cb) {
    var dbc = new DbConnection(sql, bindings, cb);
    var f = dbc.getConnection.bind(dbc);
    pool.getConnection(f);
}

물론, 이 중 어느 것도 Node.js 코딩을 사용한 적절한 JS는 아닙니다. 저는 단지 몇 시간을 보냈습니다.하지만 약간의 연마로 이 기술이 도움이 될 수 있을까요?

async.js는 이것에 잘 작동합니다.async.js의 필요성과 사용법을 예로 들어 설명하는 매우 유용한 기사를 접했습니다. http://www.sebastianseilund.com/nodejs-async-in-practice

"step" 또는 "seq"를 사용하지 않으려면 중첩된 비동기 콜백을 줄이기 위한 간단한 기능인 "line"을 사용해 보십시오.

https://github.com/kevin0571/node-line

C#과 같은 비동기 대기는 이를 수행하는 또 다른 방법입니다.

https://github.com/yortus/asyncawait

async(function(){

    var foo = await(bar());
    var foo2 = await(bar2());
    var foo3 = await(bar2());

}

와이어를 사용하면 코드는 다음과 같습니다.

http.createServer(function (req, res) {
    res.writeHead(200, {'Content-Type': 'text/html'});

    var l = new Wire();

    getSomeDate(client, l.branch('someData'));
    getSomeOtherDate(client, l.branch('someOtherData'));
    getMoreData(client, l.branch('moreData'));

    l.success(function(r) {
        res.write("<h1>Demo page</h1>"+
            "<p>"+ r['someData'] +"</p>"+
            "<p>"+ r['someOtherData'] +"</p>"+
            "<p>"+ r['moreData'] +"</p>");
        res.end();
    });
});

아시다시피 Jazz.js https://github.com/Javanile/Jazz.js/wiki/Script-showcase 을 고려해 보십시오.


const jj = required "\"js'";
초호환 스택jj.script([a => 프로세스 태스크종료 시 한 번의 콜백(a),b => 프로세스 태스크 종료 시 2콜백(b),c => 프로세스 태스크종료 시 세 번의 콜백(c),d => 프로세스 태스크 종료 시 4콜백(d),e => 프로세스 태스크 파이브 콜백엔드(e),]);

언급URL : https://stackoverflow.com/questions/4234619/how-to-avoid-long-nesting-of-asynchronous-functions-in-node-js

반응형