2017-11-19 1 views
0

나는 각각의 객체가 base64로 인코딩 된 이미지를 얻을 수있는 'url'속성을 갖는 객체의 색인화 된 배열로 구성된 응답을 클라이언트에게 다시 보내야하는 GET 경로가있는 노드/익스프레스 서버를 가지고있다. base64-img이라는 노드 라이브러리를 사용합니다.내부 비동기 코드가있는 객체의 객체를 반복 할 때 변수를 올바르게 추적하는 방법은 무엇입니까?

개체의 개체로 시작하고 각 개체의 URL 주소가 이미지 인 문자열 인 'url'속성이 있습니다. lodash을 사용하면 객체의 객체를 반복하고 있습니다. 각 반복 반복은 속성을 사용하는 base64Img.requestBase64을 호출하고 해당 URL의 대상 이미지를 base64 인코딩으로 변환합니다. 모든 객체가 반복 된 후에 객체의 인덱스 배열 (0, 1, 2 등) 인 응답을 클라이언트에 보내려고합니다.

루프가 완료되고 모든 이미지가 비동기 흐름 때문에 올바르게 인코딩 된 경우 응답을 한 번만 보내는 방법을 간단히 알 수 없습니다. 다음 코드는 때때로 전체 배열을 가끔씩 가지지 않기 때문에 작동하지 않지만 클라이언트는 항상 모든 객체를 포함하는 전체 배열이 아니라 단일 객체를 포함하는 배열을받습니다. 도와주세요.

/* 

userProfileImages = { 
    {url: 'http://www.someurl.com/blah1'}, 
    {url: 'http://www.someurl.com/blah2'}, 
    {url: 'http://www.someurl.com/blah3'}, 
    {url: 'http://www.someurl.com/blah4'} 
} 

*/ 

let completeRequests = 0; 
let arrayOfUserProfileImages = []; 
_.map(userProfileImages, (userProfileImage, key) => { 

completeRequests++; 

let tmpCount = completeRequests; 
let tmpUserProfileImage = userProfileImage; 

// loop through each of the userProfileImages, 
// get each image by the url in userProfileImage 
base64Img.requestBase64(
    tmpUserProfileImage.url, 
    function(error, messageRes, body) { 

     // if image conversion successful, proceed 
     if (!error) { 

      // get the base64 code and set it as the new 
      // value of the userProfileImage url property 
      tmpUserProfileImage.url = body; 

      // push the object into the array 
      arrayOfUserProfileImages.push(tmpUserProfileImage); 
      console.log('ARRAY LENGTH:', arrayOfUserProfileImages.length); 
      console.log(arrayOfUserProfileImages); 

      // if number of request matches the total number 
      // of requests to complete, we are done, return 
      // the appropriate json response 
      if (tmpCount === numberOfRequestsToMake) { 
       console.log(arrayOfUserProfileImages); 
       return res.json({ userProfileImages: arrayOfUserProfileImages }); 
      } 

     // else return json error 
     } else { 
      return res.json({error: error}); 
     } 

    } // end callback 

); // end base64Img 

}); // end _.map 

답변

1

루프/배열은 Promises으로 인해 훨씬 ​​쉽게 처리됩니다.

먼저 requestBase64 콜백을 약속으로 변환합니다. 콜백 함수 서명이 (error, result) 인 경우 일반적으로 util.promisify을 사용할 수 있지만이 경우 서명의 3 개 인수에는 수동 설정이 필요합니다.

function requestBase64(url){ 
    return new Promise((resolve, reject) => { 
    base64Img.requestBase64(url, (error, messageRes, body) => { 
     if (error) return reject(error) 
     resolve(body) 
    }) 
    }) 
} 

그럼 약속의 어레이를 만들고 각각의 URL을 요청하는 응답을 대기하고, URL로 응답 본체와 객체를 생성한다.

let p = _.map(userProfileImages, async (userProfileImage, key) => { 
    let body = await requestBase64(userProfileImage.url) 
    let base64userProfileImage = _.cloneDeep(userProfileImage) 
    return _.merge(base64userProfileImage, { url: body }) 
}) 

그런 다음 응답을 보내기 전에 해결하기 위해 배열의 모든 약속을 기다립니다.

try { 
    let userProfileImages = await Promise.all(p) 
    return res.json({ userProfileImages: userProfileImages }) 
} catch (error){ 
    return res.json({ error: error }) 
} 

async/await requirea Node.js 7.6+ 또는 Babel 그. .then()을 사용하는 일반 Promise 코드는 크게 다릅니다.

await을 사용하려면 async 함수 안에 있어야합니다. 이 코드를 모두 포함하고있는 기능이 무엇이든, 나는 그것이 특급 경로 처리기라고 가정하고 async이라는 태그가 필요할 것입니다.

+0

이 모든 것이 내 문제를 해결해 주셔서 감사합니다. 이러한 비동기/대기/약속 개념을 자세히 설명하는 온라인 수업 (udemy)이나 권장 자원이 있습니까? 나는이 모든 것을 정말로 알고 있으며 이것을 이해하는 데 어려움을 겪고 있습니다. 고맙습니다. –

+0

죄송합니다. 편리하지 않습니다. 아마도 비동기 함수가 먼저 어떻게 작동 하는지를 설명하는 무언가가 필요하다. 그러면 일반적인 개념을 얻은 다음이를 약속으로 변환하면 async/await가 간단한 점프가된다. – Matt

+0

다음은 [각각의 좋은 개요] (https://blog.risingstack.com/asynchronous-javascript/)이지만 인트로/튜토리얼은 그리 많지 않습니다. – Matt

관련 문제