[javascript] 비동기 자바 스크립트 함수를 동 기적으로 호출

첫째, 이것은 비동기식 호출을 수천 줄의 긴 동기식 코드베이스에 개량하기 위해 의도적으로 잘못된 방식으로 수행하는 매우 구체적인 경우입니다. 맞습니다. ” 그것은 내 존재의 모든 섬유를 다치게하지만 현실과 이상은 종종 맞물리지 않습니다. 나는 이것이 짜증나는 것을 안다.

좋아, 그 길에서, 내가 할 수 있도록 어떻게해야합니까?

function doSomething() {

  var data;

  function callBack(d) {
    data = d;
  }

  myAsynchronousCall(param1, callBack);

  // block here and return data when the callback is finished
  return data;
}

예제 (또는 그 부족)는 모두 라이브러리 및 / 또는 컴파일러를 사용하며 둘 다이 솔루션에는 적합하지 않습니다. UI를 멈추지 않고 블록을 만드는 방법에 대한 구체적인 예가 필요합니다 (예 : 콜백이 호출 될 때까지 doSomething 함수를 떠나지 마십시오). JS에서 그러한 일이 가능하다면.



답변

“내가”올바른 방법 “또는 무엇을해야하는지에 대해 말하지 마라”

확인. 하지만 당신은 정말로 올바른 방법으로해야합니다.

“UI를 멈추지 않고 차단하는 방법에 대한 구체적인 예가 필요합니다. JS에서 이런 일이 가능하다면.”

아니요, UI를 차단하지 않고 실행중인 JavaScript를 차단할 수 없습니다.

정보가 부족한 경우 솔루션을 제공하기가 어렵지만 한 가지 옵션은 호출 함수가 전역 변수를 확인하기 위해 폴링을 수행 한 다음 콜백 data을 전역으로 설정 하는 것입니다.

function doSomething() {

      // callback sets the received data to a global var
  function callBack(d) {
      window.data = d;
  }
      // start the async
  myAsynchronousCall(param1, callBack);

}

  // start the function
doSomething();

  // make sure the global is clear
window.data = null

  // start polling at an interval until the data is found at the global
var intvl = setInterval(function() {
    if (window.data) {
        clearInterval(intvl);
        console.log(data);
    }
}, 100);

이 모든 것은 당신이 수정할 수 있다고 가정합니다 doSomething(). 그게 카드에 있는지 모르겠습니다.

그것이 수정 될 수 있다면 doSomething()다른 콜백에서 콜백을 받기 위해 콜백을 전달하는 이유를 모르겠지만 문제가 발생하기 전에 멈추는 것이 좋습니다. 😉


오, 도대체. 올바르게 수행 할 수 있다고 제안하는 예를 제시 했으므로 해당 솔루션을 보여 드리겠습니다 …

function doSomething( func ) {

  function callBack(d) {
    func( d );
  }

  myAsynchronousCall(param1, callBack);

}

doSomething(function(data) {
    console.log(data);
});

예제에는 비동기 호출로 전달되는 콜백이 포함되어 있으므로 콜백에서 호출 할 함수를 전달하는 것이 올바른 방법입니다 doSomething().

물론 이것이 콜백이 수행하는 유일한 일이라면 func직접 전달할 것입니다 …

myAsynchronousCall(param1, func);


답변

ES2017 의 기능인 비동기 함수 는 약속 (특정 형식의 비동기 코드)과 await키워드 를 사용하여 비동기 코드를 동기화 된 모양으로 만듭니다 . 아래 코드 예제 에서 async / await 함수를 나타내는 키워드 async앞에있는 function키워드를 확인하십시오. await키워드는 사전을 고정하는 기능에없이 작동하지 않습니다 async키워드. 현재로서는 예외가 없으므로 최상위 레벨 대기가 작동하지 않습니다 (최상위 레벨은 함수 외부에서 대기를 의미합니다). 최상위에await 대한 제안 이 있지만 .

ES2017은 2017 년 6 월 27 일 JavaScript의 표준으로 비준 (즉 최종)되었습니다. Async await는 이미 브라우저에서 작동 할 수 있지만, 그렇지 않은 경우 babel 또는 traceur 와 같은 자바 스크립트 트랜스 파일러를 사용하여 기능을 계속 사용할 수 있습니다 . Chrome 55는 비동기 기능을 완벽하게 지원합니다. 따라서 최신 브라우저를 사용하는 경우 아래 코드를 사용해 볼 수 있습니다.

브라우저 호환성에 대해서는 kangax의 es2017 호환성 표 를 참조하십시오 .

다음은 async await 함수의 예입니다.이 함수 doAsync는 3 초의 일시 정지를 시작하고 각 일시 정지 후 시작 시간과의 시차를 인쇄합니다.

function timeoutPromise (time) {
  return new Promise(function (resolve) {
    setTimeout(function () {
      resolve(Date.now());
    }, time)
  })
}

function doSomethingAsync () {
  return timeoutPromise(1000);
}

async function doAsync () {
  var start = Date.now(), time;
  console.log(0);
  time = await doSomethingAsync();
  console.log(time - start);
  time = await doSomethingAsync();
  console.log(time - start);
  time = await doSomethingAsync();
  console.log(time - start);
}

doAsync();

await 키워드가 약속 값 앞에 놓이면 (이 경우 약속 값은 doSomethingAsync 함수에 의해 리턴 된 값임) await 키워드는 함수 호출의 실행을 일시 정지하지만 다른 함수는 일시 정지하지 않고 계속됩니다. 약속이 해결 될 때까지 다른 코드를 실행합니다. 약속이 해결 된 후에는 약속의 가치가 풀리며 대기 및 약속 표현이 현재 래핑되지 않은 값으로 대체되는 것으로 생각할 수 있습니다.

따라서 await는 대기를 일시 중지 한 다음 나머지 줄을 실행하기 전에 값을 줄 바꿈 해제하기 때문에 배열에서 기다리는 시간 차이를 수집하고 배열을 인쇄하는 아래 예제와 같이 for 루프 및 내부 함수 호출에서 사용할 수 있습니다.

function timeoutPromise (time) {
  return new Promise(function (resolve) {
    setTimeout(function () {
      resolve(Date.now());
    }, time)
  })
}

function doSomethingAsync () {
  return timeoutPromise(1000);
}

// this calls each promise returning function one after the other
async function doAsync () {
  var response = [];
  var start = Date.now();
  // each index is a promise returning function
  var promiseFuncs= [doSomethingAsync, doSomethingAsync, doSomethingAsync];
  for(var i = 0; i < promiseFuncs.length; ++i) {
    var promiseFunc = promiseFuncs[i];
    response.push(await promiseFunc() - start);
    console.log(response);
  }
  // do something with response which is an array of values that were from resolved promises.
  return response
}

doAsync().then(function (response) {
  console.log(response)
})

async 함수 자체는 약속을 반환하므로 위와 같이 또는 다른 async await 함수 내에서 체인을 사용하여 약속으로 사용할 수 있습니다.

위의 함수는 요청을 동시에 보내려면 다른 요청을 보내기 전에 각 응답을 기다릴 것 입니다. Promise.all 을 사용할 수 있습니다 .

// no change
function timeoutPromise (time) {
  return new Promise(function (resolve) {
    setTimeout(function () {
      resolve(Date.now());
    }, time)
  })
}

// no change
function doSomethingAsync () {
  return timeoutPromise(1000);
}

// this function calls the async promise returning functions all at around the same time
async function doAsync () {
  var start = Date.now();
  // we are now using promise all to await all promises to settle
  var responses = await Promise.all([doSomethingAsync(), doSomethingAsync(), doSomethingAsync()]);
  return responses.map(x=>x-start);
}

// no change
doAsync().then(function (response) {
  console.log(response)
})

약속이 거부되면 시도 catch로 랩핑하거나 시도 catch를 건너 뛰고 오류가 async / await 함수 catch 호출로 전파되도록 할 수 있습니다. 특히 Node.js에서 약속 오류를 처리하지 않은 상태로 두지 않도록주의해야합니다. 다음은 오류의 작동 방식을 보여주는 몇 가지 예입니다.

function timeoutReject (time) {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      reject(new Error("OOPS well you got an error at TIMESTAMP: " + Date.now()));
    }, time)
  })
}

function doErrorAsync () {
  return timeoutReject(1000);
}

var log = (...args)=>console.log(...args);
var logErr = (...args)=>console.error(...args);

async function unpropogatedError () {
  // promise is not awaited or returned so it does not propogate the error
  doErrorAsync();
  return "finished unpropogatedError successfully";
}

unpropogatedError().then(log).catch(logErr)

async function handledError () {
  var start = Date.now();
  try {
    console.log((await doErrorAsync()) - start);
    console.log("past error");
  } catch (e) {
    console.log("in catch we handled the error");
  }

  return "finished handledError successfully";
}

handledError().then(log).catch(logErr)

// example of how error propogates to chained catch method
async function propogatedError () {
  var start = Date.now();
  var time = await doErrorAsync() - start;
  console.log(time - start);
  return "finished propogatedError successfully";
}

// this is what prints propogatedError's error.
propogatedError().then(log).catch(logErr)

여기 로 가면 다가오는 ECMAScript 버전에 대한 완성 된 제안을 볼 수 있습니다.

ES2015 (ES6)에서만 사용할 수있는 대안은 발전기 기능을 래핑하는 특수 기능을 사용하는 것입니다. 생성기 함수에는 await 키워드를 주변 함수와 복제하는 데 사용할 수있는 yield 키워드가 있습니다. yield 키워드와 생성기 함수는 훨씬 더 일반적인 목적이며 async await 함수가 수행하는 것보다 더 많은 작업을 수행 할 수 있습니다. async await를 복제하는 데 사용할 수있는 생성기 함수 래퍼를 원한다면 co.js를 확인하십시오 . 그런데 비동기 대기 함수와 같은 co의 함수는 약속을 반환합니다. 솔직히이 시점에서 브라우저 호환성은 생성기 함수와 비동기 함수 모두에 대해 거의 동일하므로 비동기 대기 기능을 원한다면 co.js없이 비동기 함수를 사용해야합니다.

브라우저 지원은 실제로 IE를 제외한 모든 주요 현재 브라우저 (Chrome, Safari 및 Edge)의 비동기 기능 (2017 년 현재)에 매우 적합합니다.


답변

JQuery Promises를 살펴보십시오.

http://api.jquery.com/promise/

http://api.jquery.com/jQuery.when/

http://api.jquery.com/deferred.promise/

코드를 리팩터링하십시오.

    var dfd = new jQuery.Deferred ();


    함수 callBack (data) {
       dfd.notify (데이터);
    }

    // 비동기 호출을 수행합니다.
    myAsynchronousCall (param1, 콜백);

    함수 doSomething (data) {
     // 데이터로 작업 ...
    }

    $ .when (dfd) .then (doSomething);



답변

http://taskjs.org/ 에는 멋진 해결 방법이 있습니다.

자바 스크립트에 새로운 생성기를 사용합니다. 따라서 현재 대부분의 브라우저에서 구현되지 않습니다. 파이어 폭스에서 테스트했으며 비동기 기능을 래핑하는 좋은 방법입니다.

다음은 프로젝트 GitHub의 예제 코드입니다

var { Deferred } = task;

spawn(function() {
    out.innerHTML = "reading...\n";
    try {
        var d = yield read("read.html");
        alert(d.responseText.length);
    } catch (e) {
        e.stack.split(/\n/).forEach(function(line) { console.log(line) });
        console.log("");
        out.innerHTML = "error: " + e;
    }

});

function read(url, method) {
    method = method || "GET";
    var xhr = new XMLHttpRequest();
    var deferred = new Deferred();
    xhr.onreadystatechange = function() {
        if (xhr.readyState === 4) {
            if (xhr.status >= 400) {
                var e = new Error(xhr.statusText);
                e.status = xhr.status;
                deferred.reject(e);
            } else {
                deferred.resolve({
                    responseText: xhr.responseText
                });
            }
        }
    };
    xhr.open(method, url, true);
    xhr.send();
    return deferred.promise;
}


답변

당신은 할 수 와 동기로 NodeJS에서 비동기 자바 스크립트를 강제로 동기화-RPC .

그것은 확실히 UI를 고정시킬 것이므로, 필요한 단축키를 사용할 수 있는지 여부에 관해서는 여전히 선구자입니다. NodeJS에서 가끔 차단할 수 있어도 JavaScript에서 One And Only Thread를 일시 중단 할 수 없습니다. 약속이 해결 될 때까지 콜백, 이벤트 및 비동기 항목을 처리 할 수 ​​없습니다. 따라서 독자가 OP와 같은 피할 수없는 상황이 아니거나 콜백, 이벤트 등이없는 영광스러운 쉘 스크립트를 작성하지 않는 한이를 수행하지 마십시오!

그러나이 작업을 수행하는 방법은 다음과 같습니다.

./calling-file.js

var createClient = require('sync-rpc');
var mySynchronousCall = createClient(require.resolve('./my-asynchronous-call'), 'init data');

var param1 = 'test data'
var data = mySynchronousCall(param1);
console.log(data); // prints: received "test data" after "init data"

./my-asynchronous-call.js

function init(initData) {
  return function(param1) {
    // Return a promise here and the resulting rpc client will be synchronous
    return Promise.resolve('received "' + param1 + '" after "' + initData + '"');
  };
}
module.exports = init;

제한 사항 :

이 두 가지 모두 sync-rpc구현 방법의 결과입니다 require('child_process').spawnSync.

  1. 브라우저에서는 작동하지 않습니다.
  2. 함수에 대한 인수 직렬화 가능 해야 합니다. 인수는에서 전달되거나 전달 JSON.stringify되지 않으므로 프로토 타입 체인과 같은 함수 및 열거 할 수없는 특성이 손실됩니다.

답변

콜백으로 변환 할 수도 있습니다.

function thirdPartyFoo(callback) {
  callback("Hello World");
}

function foo() {
  var fooVariable;

  thirdPartyFoo(function(data) {
    fooVariable = data;
  });

  return fooVariable;
}

var temp = foo();
console.log(temp);


답변

당신이 원하는 것은 실제로 가능합니다. 서비스 워커에서 비동기 코드를 실행하고 웹 워커에서 동기 코드를 실행할 수 있으면 웹 워커가 서비스 워커에게 동기 XHR을 보내도록하고 서비스 워커가 비동기 작업을 수행하는 동안 웹 워커의 스레드가 기다립니다. 이것은 훌륭한 접근 방법은 아니지만 효과가 있습니다.