programing

Node.js 또는 Javascript에서 비동기 함수 호출을 동기 함수로 랩하는 방법은 무엇입니까?

cafebook 2023. 8. 11. 22:33
반응형

Node.js 또는 Javascript에서 비동기 함수 호출을 동기 함수로 랩하는 방법은 무엇입니까?

를 공개하는 라이브러리를 유지한다고 가정해 .getData사용자는 실제 데이터를 얻기 위해 이를 호출합니다.
var output = getData();
은 후드아데저구어습다니었을 구현했습니다.getData Node 파일 사용.js 파일 사용fs.readFileSync 니다합분명니다getData그리고.fs.readFileSync동기화 기능입니다.하루는 기본 데이터 소스를 비동기식으로만 액세스할 수 있는 MongoDB와 같은 레포로 전환하라는 지시를 받았습니다.당신은 또한 당신의 사용자들을 화나게 하지 말라고 들었습니다.getData약속만 반환하거나 콜백 매개 변수를 요구하도록 API를 변경할 수 없습니다.두 가지 요구 사항을 모두 충족하는 방법은 무엇입니까?

콜백/약속을 사용하는 비동기 함수는 JavasScript 및 Node.js의 DNA입니다.사소하지 않은 JS 앱은 아마도 이 코딩 스타일에 스며 있을 것입니다.그러나 이러한 관행은 소위 '운명의 콜백 피라미드'로 쉽게 이어질 수 있습니다.더 나쁜 것은, 콜 체인의 호출자 중 어떤 코드가 비동기 함수의 결과에 의존한다면, 그 코드는 콜백 함수로 감싸여 호출자에게 코딩 스타일 제약을 가해야 한다는 것입니다.대규모 글로벌 리팩터링을 방지하기 위해 때때로 비동기 함수(타사 라이브러리에서 제공되는 경우가 많음)를 동기 함수로 캡슐화할 필요가 있습니다.이 주제에 대한 솔루션을 검색하면 일반적으로 노드 파이버 또는 이에서 파생된 npm 패키지로 끝납니다.하지만 섬유는 제가 직면한 문제를 해결할 수 없습니다.Fibers의 저자가 제공한 예에서도 그 결함을 설명했습니다.

...
Fiber(function() {
    console.log('wait... ' + new Date);
    sleep(1000);
    console.log('ok... ' + new Date);
}).run();
console.log('back in main');

실제 출력:

wait... Fri Jan 21 2011 22:42:04 GMT+0900 (JST)
back in main
ok... Fri Jan 21 2011 22:42:05 GMT+0900 (JST)

function Fiber가 정말로 비동기 function sleep을 sync로 전환하는 경우 출력은 다음과 같아야 합니다.

wait... Fri Jan 21 2011 22:42:04 GMT+0900 (JST)
ok... Fri Jan 21 2011 22:42:05 GMT+0900 (JST)
back in main

는 JSFidle에서 또 다른 간단한 예제를 만들어 예상 결과를 산출할 수 있는 코드를 찾았습니다.Node.js에서만 작동하는 솔루션을 수락하여 JSFidle에서 작동하지 않더라도 npm 패키지를 자유롭게 요구할 수 있습니다.

deasync는 JavaScript 계층에서 Node.js 이벤트 루프를 호출하여 차단 메커니즘으로 구현된 비동기 기능을 동기화합니다.결과적으로, deasync는 전체 스레드를 차단하거나 사용량이 많은 대기를 유발하지 않고 후속 코드의 실행을 차단할 뿐입니다.이 모듈을 사용하여 jsFiddle 과제에 대한 답은 다음과 같습니다.

function AnticipatedSyncFunction(){
  var ret;
  setTimeout(function(){
      ret = "hello";
  },3000);
  while(ret === undefined) {
    require('deasync').runLoopOnce();
  }
  return ret;    
}


var output = AnticipatedSyncFunction();
//expected: output=hello (after waiting for 3 sec)
console.log("output="+output);
//actual: output=hello (after waiting for 3 sec)

(문자:저는 의 공동 저자입니다.deasync모듈은 이 질문을 게시한 후 생성되었지만 실행 가능한 제안을 찾을 수 없습니다.)

약속을 사용해야 합니다.

const asyncOperation = () => {
    return new Promise((resolve, reject) => {
        setTimeout(()=>{resolve("hi")}, 3000)
    })
}

const asyncFunction = async () => {
    return await asyncOperation();
}

const topDog = () => {
    asyncFunction().then((res) => {
        console.log(res);
    });
}

저는 화살표 함수 정의가 더 좋습니다.그러나 형식의 문자열(") => {...함수() {...}로 쓸 수도 있습니다.

따라서 TopDog는 비동기 함수를 호출해도 비동기가 아닙니다.

enter image description here

편집: 비동기 함수를 컨트롤러 내부에 감아야 하는 경우가 많다는 것을 알고 있습니다.이러한 상황을 위해 파티 트릭이 있습니다.

const getDemSweetDataz = (req, res) => {
    (async () => {
        try{
            res.status(200).json(
                await asyncOperation()
            );
        }
        catch(e){
            res.status(500).json(serviceResponse); //or whatever
        }
    })() //So we defined and immediately called this async function.
}

이를 콜백과 함께 활용하면 약속을 사용하지 않는 랩을 수행할 수 있습니다.

const asyncOperation = () => {
    return new Promise((resolve, reject) => {
        setTimeout(()=>{resolve("hi")}, 3000)
    })
}

const asyncFunction = async (callback) => {
    let res = await asyncOperation();
    callback(res);
}

const topDog = () => {
    let callback = (res) => {
        console.log(res);
    };

    (async () => {
        await asyncFunction(callback)
    })()
}

이 방법을 이벤트 송신기에 적용하면 동일한 결과를 얻을 수 있습니다.콜백을 정의한 이벤트 송신기의 수신기를 정의하고 콜백을 호출한 이벤트를 내보냅니다.

만약 기능 섬유가 정말로 비동기 기능 슬립을 동기화로 바꾼다면,

가 logging 하기 에 기능이 합니다. 네부내대전에다기니합기기록하기능은섬서유에▁yes다▁waits니,대기네합▁before▁inside▁function▁fiber▁the▁the▁logging전에기기섬.ok동기화하지 하여 비동기식으로 할 수 . 그런 다음 내부에서 비동기식으로 실행됩니다.Fiber.

대규모 글로벌 리팩터링을 피하기 위해 비동기 함수를 동기 함수로 캡슐화할 필요가 있습니다.

안 돼요.비동기 코드를 동기화할 수 없습니다.당신은 당신의 글로벌 코드에서 그것을 예상하고, 처음부터 비동기식으로 작성해야 합니다.글로벌 코드를 섬유로 감쌀지, 약속을 사용할지, 약속 생성기를 사용할지 또는 단순 콜백을 사용할지는 사용자의 기본 설정에 따라 달라집니다.

내 목표는 데이터 수집 방법이 동기화에서 비동기화로 변경될 때 호출자에게 미치는 영향을 최소화하는 것입니다.

약속과 섬유 둘 다 그것을 할 수 있습니다.

npm 동기화 모듈도 있습니다.쿼리 실행 프로세스를 동기화하는 데 사용됩니다.

병렬 쿼리를 동기식으로 실행하려는 경우 노드는 응답을 기다리지 않으므로 이를 수행하도록 제한합니다.동기화 모듈은 이러한 솔루션에 매우 적합합니다.

샘플코드

/*require sync module*/
var Sync = require('sync');
    app.get('/',function(req,res,next){
      story.find().exec(function(err,data){
        var sync_function_data = find_user.sync(null, {name: "sanjeev"});
          res.send({story:data,user:sync_function_data});
        });
    });


    /*****sync function defined here *******/
    function find_user(req_json, callback) {
        process.nextTick(function () {

            users.find(req_json,function (err,data)
            {
                if (!err) {
                    callback(null, data);
                } else {
                    callback(null, err);
                }
            });
        });
    }

참조 링크: https://www.npmjs.com/package/sync

오늘날 이러한 제너레이터 패턴은 많은 상황에서 해결책이 될 수 있습니다.

다음은 비동기 리드라인을 사용하는 nodejs의 순차적 콘솔 프롬프트 예입니다.질문 함수:

var main = (function* () {

  // just import and initialize 'readline' in nodejs
  var r = require('readline')
  var rl = r.createInterface({input: process.stdin, output: process.stdout })

  // magic here, the callback is the iterator.next
  var answerA = yield rl.question('do you want this? ', r=>main.next(r))    

  // and again, in a sync fashion
  var answerB = yield rl.question('are you sure? ', r=>main.next(r))        

  // readline boilerplate
  rl.close()

  console.log(answerA, answerB)

})()  // <-- executed: iterator created from generator
main.next()     // kick off the iterator, 
                // runs until the first 'yield', including rightmost code
                // and waits until another main.next() happens

노드 파이버를 사용하여 해결할 수 없는 시나리오를 찾을 수 없습니다.노드 파이버를 사용하여 제공한 예제는 예상대로 작동합니다.핵심은 파이버 내에서 모든 관련 코드를 실행하여 임의의 위치에서 새로운 파이버를 시작할 필요가 없도록 하는 것입니다.

예를 들어 보겠습니다.응용프로그램의 진입점인 일부 프레임워크를 사용한다고 가정합니다(이 프레임워크는 수정할 수 없음).이 프레임워크는 nodejs 모듈을 플러그인으로 로드하고 플러그인에서 몇 가지 메서드를 호출합니다.이 프레임워크가 동기식 기능만 허용하고 섬유 자체를 사용하지 않는다고 가정해 보겠습니다.

플러그인 중 하나에서 사용하려는 라이브러리가 있지만 이 라이브러리는 비동기식이므로 수정하지 않을 수도 있습니다.

파이버가 실행되고 있지 않을 때는 메인 스레드를 생성할 수 없지만 파이버를 사용하여 플러그인을 생성할 수 있습니다!파이버 내부의 전체 프레임워크를 시작하는 래퍼 항목을 만들기만 하면 플러그인에서 실행할 수 있습니다.

단점:프레임워크가 다음을 사용하는 경우setTimeout또는Promise내부적으로, 그러면 그것은 섬유 맥락에서 벗어날 것입니다.이 문제는 조롱을 통해 해결할 수 있습니다.setTimeout,Promise.then및 모든 이벤트 처리기.

그래서 이것이 당신이 섬유를 생산할 수 있는 방법입니다.Promise해결되었습니다. 되돌리기)합니다.

framework-entry.js

console.log(require("./my-plugin").run());

비동기-lib.js

exports.getValueAsync = () => {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve("Async Value");
    }, 100);
  });
};

나의-나의.js.

const Fiber = require("fibers");

function fiberWaitFor(promiseOrValue) {
  var fiber = Fiber.current, error, value;
  Promise.resolve(promiseOrValue).then(v => {
    error = false;
    value = v;
    fiber.run();
  }, e => {
    error = true;
    value = e;
    fiber.run();
  });
  Fiber.yield();
  if (error) {
    throw value;
  } else {
    return value;
  }
}

const asyncLib = require("./async-lib");

exports.run = () => {
  return fiberWaitFor(asyncLib.getValueAsync());
};

마이엔트리.js.

require("fibers")(() => {
  require("./framework-entry");
}).run();

을 할 때node framework-entry.js합니다: " 오가발생다니.Error: yield() called with no fiber running실행하는 경우node my-entry.js예상대로 작동합니다.

여러분은 섬유를 생성하는 통화 주변에서 일어나는 일을 보는 것이 아니라 섬유 내부에서 일어나는 일을 봐야 합니다.섬유 내부에 들어가면 동기화 스타일로 프로그래밍할 수 있습니다.예:

함수 f1은 {}을(를) 포함합니다.console.log, wait...+ 새 날짜);수면(1000);console.log('ok...+ 새 날짜);}
함수 f2는 {}을(를) 나타냅니다.f1ppm;f1ppm;}
섬유(함수() {f2ppm;}).run();

은 신이부르섬안유는당라고 .f1,f2그리고.sleep마치 그들이 동기화된 것처럼.

일반적인 웹 응용프로그램에서 HTTP 요청 디스패처에 파이버를 만듭니다.이 작업을 완료하면 비동기 함수(fs, 데이터베이스 등)를 호출하더라도 모든 요청 처리 로직을 동기화 스타일로 작성할 수 있습니다.

처음에는 node.js와 씨름했지만 async.js는 이 문제를 해결하는 데 도움이 되는 최고의 라이브러리입니다.노드와 동기화 코드를 작성하려면 이 방법을 사용합니다.

var async = require('async');

console.log('in main');

doABunchOfThings(function() {
  console.log('back in main');
});

function doABunchOfThings(fnCallback) {
  async.series([
    function(callback) {
      console.log('step 1');
      callback();
    },
    function(callback) {
      setTimeout(callback, 1000);
    },
    function(callback) {
      console.log('step 2');
      callback();
    },
    function(callback) {
      setTimeout(callback, 2000);
    },
    function(callback) {
      console.log('step 3');
      callback();
    },
  ], function(err, results) {
    console.log('done with things');
    fnCallback();
  });
}

이 프로그램은 항상 다음을 생성합니다.

in main
step 1
step 2
step 3
done with things
back in main

Node.js 코드 동기화는 데이터베이스와 같은 몇 가지 측면에서 필수적입니다.그러나 Node.js의 실제 이점은 비동기 코드에 있습니다.싱글 스레드 논블로킹이므로.

중요한 기능을 사용하여 동기화할 수 있습니다. fiber() wait() 및 delay()를 사용하여 모든 메서드를 wait()라고 합니다. 그런 다음 콜백 함수를 delay()로 바꿉니다.

일반 비동기 코드입니다.이것은 콜백 기능을 사용합니다.

function add (var a, var b, function(err,res){
       console.log(res);
});

 function sub (var res2, var b, function(err,res1){
           console.log(res);
    });

 function div (var res2, var b, function(err,res3){
           console.log(res3);
    });

Fiber(), wait() 및 delay()를 사용하여 위의 코드를 동기화합니다.

fiber(function(){
     var obj1 = await(function add(var a, var b,defer()));
     var obj2 = await(function sub(var obj1, var b, defer()));
     var obj3 = await(function sub(var obj2, var b, defer()));

});

이것이 도움이 되길 바랍니다.감사해요.

언급URL : https://stackoverflow.com/questions/21819858/how-to-wrap-async-function-calls-into-a-sync-function-in-node-js-or-javascript

반응형