2017-09-21 9 views
4

비동기/대기 전에 코드에서 콜백을 사용하면 다음과 같은 세 가지 작업을 수행 할 수있었습니다. (1) 결과로 콜백을 호출합니다. (2) 콜백을 Error로 호출합니다. 또는 (3) 콜백을 호출하지 않습니다. 모든.비동기 함수 내부에서 콜백을 호출하지 않는 것과 동일한 것은 무엇입니까?

사례 (3)은 다음과 같은 상황에서 사용되었습니다. 사용자가 확대/축소 버튼을 가지고 있으며이를 클릭하여 더 높은 해상도로 이미지를 렌더링 할 수 있으며 이는 비동기 프로세스입니다. 사용자가 확대/축소 버튼을 다시 클릭하면 첫 번째 렌더링과 더 이상 관련이 없으며 새 줌 레벨 렌더링이 실행되도록 취소 할 수 있습니다. 콜백을 호출하지 않고 함수 내부에서 돌아가서 처리했습니다.

if (process.wasCanceled()) { 
    return; 
} 
// ... 
callback(someResult); 

async/await를 사용하면 수행 할 수있는 두 가지 작업 만 수행하거나 반환 할 수 있습니다. 현재, 작업이 취소되었다는 것을 나타 내기 위해 throw를 사용했습니다. 반환은 업스트림 프로세스가 계속 실행되어야한다는 잘못된 정보를 나타낼 수 있기 때문입니다. 그러나 던지는 문제는 모든 상류 호출자가 실제로 오류가 아니라는 것을 알아야하기 때문에 오류 유형을 확인해야 할 수도 있습니다.

내가 가진 또 다른 미친 아이디어는 결코 반환하지 않는 약속을 만드는 것이 었습니다. 예 : 함수가 다음과 같이 정의된다 await never() :

콜백을 호출하지 동등한 일종의
async function never() { 
    return new Promise(function() {}); 
} 

.

하지만 메모리가 계속해서 새어 나올지는 모르겠습니다.

위에서 언급 한 단점이없는 더 나은 기능이 있습니까?

+2

"cancelled"와 같은 특정 메시지와 함께 throw를 사용하는 것이 좋습니다. 네, 모든 업스트림이 이것을 찾아야합니다 ...하지만 당신이 원하는 것은 아닌가요? – ControlAltDel

+2

이것은 정말 흥미로운 질문입니다. 나는'never()'가 메모리 누출 이유에 대해 매우 나쁜 생각이라고 믿는다. @ControlAltDel 그의 첫 번째 예제에서'process.wasCanceled()'와 같이 콜백이 실행되지 않기 때문에 업스트림 *을 보지 않아도되는 상황을 원한다고 생각합니다. – TKoL

+0

''await never()'에 대해 생각하면 더 좋을 것 같습니다. 이 두 가지 질문 (https : // stackoverflow.com/questions/20068467/및 https://stackoverflow.com/questions/38286358)는 브라우저가 'never'함수로 만든 약속을 가비지 수집 할 수 있음을 나타냅니다. 약속 함수에 연결하는 함수가 없으므로 나머지 힙. 그래서 아마도 누출되지 않습니다. –

답변

1

절대적으로 필요한 경우 결코 반환하지 않는 약속을 기다릴 수 있습니다. 이것은 콜백을 호출하지 않는 것과 같습니다. 반환 약속을 사용하지 않기 때문에 이러한 답변에 따르면

async function never() { 
    return new Promise(function() {}); 
} 

async function op (process) { 
    // ... 
    if (process.wasCanceled()) await never(); 
    // ... 
} 

,이 쓰레기 수집되고 약속의 함수 인수 내부 힙에는 연결이 없습니다.

Are JavaScript forever-pending promises bad?

Do never resolved promises cause memory leak? 그러나, 이것은 당신이 상류 발신자가 자신의 작업이 취소 된 것을 알고 싶어 수 있기 때문에,하고 싶지 무엇을 가능성이 높습니다. UI를 통해 사용자가 작업을 시작한 경우 예, 호출자에게 알리지 않고 취소하는 것은 좋을 수 있지만 프로그래밍 방식으로 작업이 시작되고 다른 방식으로 취소 된 경우 (예 : 사용자가 호출 코드를 알 필요가 있으므로 다시 시도하거나 자원을 정리할 수 있습니다.

이러한 이유로 솔루션은 호출자가 프로세스가 취소되었음을 감지 할 수 있도록 특정 클래스의 오류를 throw합니다. 예 :

class ProcessCanceledError extends Error { 
    ... 
} 

async function render (process) { 
    while (...) { 
     // do some rendering 
     await delay(20); 
     if (process.wasCanceled()) throw new ProcessCanceledError(); 
    } 
} 

var zoomProcess; 

async function zoom() { 
    let process = new Process(); 
    if (zoomProcess != null && !zoomProcess.isDone()) { 
     zoomProcess.cancel(); 
    } 
    try { 
     await render(); 
    } catch (e) { 
     // or you could do e.process === process 
     if (e instanceof ProcessCanceledError && 
      process.wasCanceled() // make sure it was actually ours 
     ) { 
      // this assumes we are a top level function 
      // otherwise, you would want to propagate the error to caller's caller 
      return; 
     } 
     throw e; 
    } 
}