2017-10-24 2 views
0

은 제가 생산이 쓰레기 처리 도와주세요 리소스 로더에서 콜백 지옥 :WebGL을 :

Program.prototype.init = function() 
{ 
loadText('../res/shaders/blinnPhong-shader.vsh', function (vshErr, vshText) { 
    if (vshErr) { 
     alert('Fatal error loading vertex shader.'); 
     console.error(vshErr); 
    } else { 
     loadText('../res/shaders/blinnPhong-shader.fsh', function (fshErr, fshText) { 
      if (fshErr) { 
       alert('Fatal error loading fragment shader.'); 
       console.error(fshErr); 
      } else { 
       loadJSON('../res/models/dragon.json', function (modelErr, modelObj) { 
        if (modelErr) { 
         alert('Fatal error loading model.'); 
         console.error(modelErr); 
        } else { 
         loadImage('../res/textures/susanTexture.png', function (imgErr, img) { 
          if (imgErr) { 
           alert('Fatal error loading texture.'); 
           console(imgErr); 
          } else { 
           this.run = true; 
           RunProgram(vshText, fshText, img, modelObj); 
          } 
         }); 
        } 
       }); 
      } 
     }); 
    } 
}); 
}; 

내 실제 목표는 WebGL이 프로그램에 대한 추상적 인 자원로드 프로세스이다. 즉, 메시, 텍스처, 쉐이더 배열이있을 것이며, 리소스간에 특정 종속성을 연결할 수 있기를 바랍니다. 예 : 두 개의 GameObjects One과 Two를 만들고 싶습니다. 하나는 셰이더를 사용하고 메쉬에서로드되지만 텍스처가없는 반면 2는 동일한 셰이더를 One로 사용하지만 자체 메쉬를 사용하며 텍스처가 필요합니다. JavaScript (비동기 적재 등)에서 이러한 종속성을 구축하기 위해 어떤 원칙을 사용할 수 있습니까?

편집 : 그래서이 코드에서 다음과 같은 현상이 발생합니다. 현재 콜백을 유지했습니다. 그러나이 메서드는 Singleton 개체의 일부입니다. 나는 마지막으로 다른 경우에 프로그램의 플래그를 true로 설정하기 때문에 코드를 편집했습니다. 내 메인에 프로그램 개체의 글로벌 참조를 유지합니다. 그러나 콜백으로 인해 참조가 어떻게 든 손실되며 전역 참조는 플래그를 false로 유지하므로 기본 루프에는 도달하지 않습니다. 중첩 콜백 바깥에서 "this.run = true"를 호출하면 플래그가 설정되므로 분명히 콜백의 문제점입니다. 그것에 대한 조언이 있습니까?

+0

첫 번째 조언은 질문을 업데이트하지 않고 응답을받은 후에 범위를 변경하는 것입니다. 동료 개발자가 검색 엔진을 통해 질문을 찾는 것과 마찬가지로 질문이 많은 것입니다. 둘째 [이 대답 읽기] (https://stackoverflow.com/a/500459/978057), 세 번째는 주어진 답변 중 하나를 수락하는 것입니다.) –

+0

TLDR은 'this'는 함수의 범위를 나타냅니다 현재 콜드 백에 대한 콜백을 ['bind']해야합니다 (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind). 원래 스코프를 저장하거나 Program.Init 메소드의 맨 위에'this'에 대한 참조를 저장하고 더 깊게 스택을 사용하십시오. –

답변

2

Promises 같은 현대적인 API를 사용하여 모든 약속의 배열, Fetch 및 설탕 arrow functions과 같은 코드는 다음과 같이 표시 될 수 있습니다.

Program.prototype.init = function() { 
    return Promise.all(
     fetch('../res/shaders/blinnPhong-shader.vsh').then(r=>r.text()), 
     fetch('../res/shaders/blinnPhong-shader.fsh').then(r=>r.text()), 
     fetch('../res/models/dragon.json').then(r=>r.json()), 
     new Promise(function (resolve,reject) { 
      var i = new Image(); 
      i.onload =() => resolve(i); 
      i.onerror =() => reject('Error loading image '+i.src); 
      i.src = '../res/textures/susanTexture.png'; 
     }) 
    ) 
    .then(RunProgram); 
} 

async functions/await과 같은 관련 ES2017 기능을 사용하거나 화살표 기능으로 호환성을 올리거나 약속 및 가져 오기에 원활한 폴리필을 사용하여 더 많은 것을 자극 할 수 있습니다. 몇 가지 간단한 요청 캐싱의 경우, 가져 오기 포장 : 당신이 당신의 자원을 고유 할

const fetchCache = Object.create(null); 
function fetchCached (url) { 
    if (fetchCache[url]) 
     return Promise.resolve(fetchCache[url]); 
    return fetch.apply(null,arguments).then(r=>fetchCache[url]=r); 
} 

참고 때문에 위에서 언급 한 캐싱은 여전히 ​​그 위에 실제 GPU 리소스 캐싱의 또 다른 레이어를 필요로, 당신은하고 싶지 않아 동일한 버텍스 데이터를 가진 동일한 셰이더 코드 또는 배열 버퍼를 가진 여러 셰이더 프로그램을 만듭니다.

종속성을 어떻게 관리 할 수 ​​있는지에 대한 실제 핵심 질문은 너무 광범위하기 때문에/여기서는 답변을 얻지 못했습니다. 이러한 환경에서 비동기 자연을 관리에 관해서 나는 두 가지 옵션을 참조하십시오

  1. 사용 자리 자원을 원활하게 당신이 게임 오브젝트 삽입하기 전에 모든로드 될 때까지 실제 자원
  2. 대기로드되면 교체 렌더링 파이프 라인에

두 방법 모두 장단점이 있지만 일반적으로 첫 번째 옵션을 권합니다.

+0

이 기능은 "modern"최신 API를 "sugars"와 함께 사용하지만 코드는 일반적인 콜백보다 훨씬 읽기 쉽습니다. 그러나 이것은 사용자의 잘못이 아니며, 이것이 Javascrit가 권장하는 방식입니다. –

+0

@AnHauntingGhostAsPromise "어떻게하면 읽을 수 없습니까?" 수직 및 수평 공간을 많이 절약 할 수 있습니다. 또한 모든 설탕이 아닌이 코드는 요청이 병렬로 처리되는 동안 원본이 순서대로 처리합니다. 또한이 코드를 사용하면 한 곳에서 모든 오류를 처리 할 수 ​​있으며 거부 요청 처리기를 개별 요청에 첨부하여 대체 오류 및 개별 오류 처리를 구현할 수 있습니다. –

+0

코드의 가독성은 텍스트가 화면에서 차지하는 수직 및 수평 공간에 의해 측정되는 것이 아니라 각 단계, 함수 호출 등의 기본 논리를 이해하기 쉽고 어떻게 누가, 누가 누가, 어떤 순서로 전화하는지 등등. 이것은 예를 들어 디버그 컨텍스트에서의 유틸리티를가집니다. 그래서 예,'this.method (obj.func (function (foo) {return (foo> bar)? true : false;}))'와 같은 코드를 작성할 수 있습니다. 그러나 이것은 읽을 수 없습니다. 당신이 쓴 글은 심지어 유효한 자바 스크립트 일지라도 "스파게티 코드"에 가깝지만 구조적으로 자바 스크립트는 그러한 방향으로 당신을 밀어 붙입니다. –

2

이것에 대한 약속을 사용할 수 있습니다. bluebird 모듈을, 당신이 promise.promiseifyAll (모듈 loadText부터이다), 또는 당신의 모듈 인 경우, 당신은 그것이 new Promise(function(resolve, reject){})

사용하여 약속을 반환 할 수있는 약속의 기능에 loadText을 변환 할 수 있습니다, 당신은 할 수 있습니다 실행할 및 Promise.all ([loadText ('쉐이더'), loadText ("다른 쉐이더"), ...])

More information on promises