비동기 함수와 AngularJs 1.X가 혼합되지 않음

11079 단어 javascriptangular

Originally posted on ivantanev.com



최근에 AngularJS 1.x 프로젝트를 리팩토링하고 다음 코드를 작성했습니다.

// DO NOT USE THIS CODE, IT BREAKS ANGULARJS!
// Combining $http with async functions does not work in AngularJS 1.X
async function updateItem(item) {
  const { data } = await $http.put(`/items/${item.id}`, item)
  return data
}


위의 내용은 충분히 순진해 보이지만 Angular의 다이제스트 주기를 깨뜨립니다. 약속이 이행되면 페이지 업데이트가 표시되지 않습니다.

대신 무엇을 해야 합니까?


await/async$http과 함께 사용하지 마십시오. 대신 .then() 와 함께 이전 약속 스타일을 사용하십시오.

// Use promise.then() instead of async/await
function updateItem(item) {
  return $http.put(`/items/${item.id}`, item).then(({ data }) => data)
}


위의 내용이 완전히 합리적이고 현대적인 JavaScript처럼 보일 수 있지만 이를 사용하려고 하면 Angular의 다이제스트 주기가 중단됩니다.

어디가 깨지나요?



브라우저가 async/await 코드를 실행할 때 수행하는 작업을 살펴보겠습니다.

// DO NOT USE THIS CODE, IT BREAKS ANGULARJS!
// Combining $http with async functions does not work in AngularJS 1.X

// This function:
async function updateItem(item) {
  const { data } = await $http.put(`/items/${item.id}`, item)
  return data
}


// Is equivalent to the following code:
function updateItem(item) {
  return Promise.resolve($http.put(`/items/${item.id}`, item)).then((value) => {
    const { data } = value
    return data
  })
}


보시다시피 $q 에서 반환된 원래 $http.put() 약속은 새 Promise.resolve() 로 래핑됩니다. 이것은 AngularJS가 약속이 정착될 때 더 이상 추적할 수 없음을 의미합니다.

컨트롤러에서 비동기 버전updateItem()을 사용하려고 하면 문제가 발생합니다.

function SomeCtrl($scope, initialItem) {
  $scope.item = initialItem

  this.onClick = function() {
    updateItem($scope.item)
      .then(updatedItem => ($scope.item = updatedItem))
  }
}


The async function implementation of updateItem() would break the highlighted line. The changed $scope.item variable will not be reflected in the DOM, or watchers, until a random digest cycle executes.



그 이유는 AngularJS가 일반 약속이 해결되는 시점을 알 수 없기 때문입니다.



AngularJS에는 브라우저 프리미티브($timeout, $interval 및 자체 Promise 라이브러리$q에 대한 특수 래퍼가 있습니다. AngularJS는 완료 시점을 추적하고 $rootScope.$digest() 주기를 실행하기 위해 이러한 비동기 인터페이스를 래핑해야 합니다.
async function 를 사용했을 때 우리는 setTimeout() 대신 $timeout()를 직접 사용한 것과 같은 상황에 처해 있습니다. AngularJS가 비동기 함수 실행이 완료된 시점을 추적할 방법이 없습니다.

컨트롤러에서 async function가 작동하도록 하려면 $q.resolve()로 다시 래핑해야 합니다.

function SomeCtrl($q, $scope, initialItem) {
  $scope.item = initialItem

  this.onClick = function() {
    $q.resolve(updateItem($scope.item)).then(
      updatedItem => ($scope.item = updatedItem)
    )
  }
}


또는 컨트롤러 속성 할당 주위에 다른 async function$apply()을 사용할 수 있습니다.

function SomeCtrl($scope, initialItem) {
  $scope.item = initialItem

  this.onClick = async function() {
    const updatedItem = await updateItem($scope.item)
    $scope.$apply(() => {
      $scope.item = updatedItem
    })
  }
}

async function 코드의 효과를 $scope.$apply() 로 수동으로 래핑하거나 $q.resolve() 로 약속을 래핑해야 합니다. 이것은 처음에 async/await를 사용할 가치가 없습니다. async/await 인터페이스가 훨씬 더 좋기 때문에 여러 비동기 작업을 조정해야 할 때 이것은 불행한 일입니다.

결론적으로



모던async/await 함수는 훌륭하고 오래된 코드를 리팩토링할 때 사용하고 싶은 유혹이 있습니다. 그러나 AngularJS 1.X 프로젝트에서 번거롭게 할 가치는 없습니다. 대신 $q 약속 인터페이스를 고수해야 합니다.

my website 에서 인사하거나 다른 게시물을 확인하세요.
즐거운 코딩!

좋은 웹페이지 즐겨찾기