[javascript] 약속 체인을 끊고 체인이 끊어진 (거부 된) 단계에 따라 함수를 호출하십시오.

최신 정보:

이 게시물의 향후 시청자를 돕기 위해 나는 pluma의 답변 데모를 만들었습니다 .

질문:

나의 목표는 매우 간단 해 보인다.

  step(1)
  .then(function() {
    return step(2);
  }, function() {
    stepError(1);
    return $q.reject();
  })
  .then(function() {

  }, function() {
    stepError(2);
  });

  function step(n) {
    var deferred = $q.defer();
    //fail on step 1
    (n === 1) ? deferred.reject() : deferred.resolve();
    return deferred.promise;
  }
  function stepError(n) {
    console.log(n);
  }

여기서 문제는 1 단계에서 실패하면 stepError(1)AND stepError(2)가 모두 발생한다는 것 입니다. 내가하지 않으면 return $q.reject다음 stepError(2)발사,하지만하지 않습니다 step(2)이해하는 것입니다. 내가하려는 일을 제외하고 모든 것을 성취했습니다.

오류 체인의 모든 함수를 호출하지 않고 거부시 함수를 호출 할 수 있도록 약속을 작성하는 방법은 무엇입니까? 아니면 이것을 달성하는 다른 방법이 있습니까?

여기에 데모가 있으므로 작업이 필요합니다.

최신 정보:

나는 가지 를 해결했다. 여기서는 체인 끝에서 오류를 포착하고 데이터를 전달 reject(data)하여 오류 기능에서 처리해야 할 문제를 알 수 있습니다. 데이터에 의존하고 싶지 않기 때문에 실제로 요구 사항을 충족시키지 못합니다. 그것은 절름발이이지만 내 경우에는 반환 된 데이터에 의존하여 수행 할 작업을 결정하는 대신 오류 콜백을 함수에 전달하는 것이 더 깨끗합니다.

여기에서 라이브 데모를 클릭하십시오 (클릭).

step(1)
  .then(function() {
    return step(2);
  })
  .then(function() {
    return step(3);
  })
  .then(false,
    function(x) {
      stepError(x);
    }
  );
  function step(n) {
    console.log('Step '+n);
    var deferred = $q.defer();
    (n === 1) ? deferred.reject(n) : deferred.resolve(n);
    return deferred.promise;
  }
  function stepError(n) {
    console.log('Error '+n);
  }



답변

코드가 예상대로 작동하지 않는 이유는 실제로 생각한 것과 다른 것을 수행하기 때문입니다.

다음과 같은 것이 있다고 가정 해 봅시다.

stepOne()
.then(stepTwo, handleErrorOne)
.then(stepThree, handleErrorTwo)
.then(null, handleErrorThree);

무슨 일이 일어나고 있는지 더 잘 이해하기 위해 try/ catch블록 이있는 동기 코드 인 척하십시오 .

try {
    try {
        try {
            var a = stepOne();
        } catch(e1) {
            a = handleErrorOne(e1);
        }
        var b = stepTwo(a);
    } catch(e2) {
        b = handleErrorTwo(e2);
    }
    var c = stepThree(b);
} catch(e3) {
    c = handleErrorThree(e3);
}

onRejected핸들러 (두 번째 인수 then) 본질적으로 (a 같은 에러 정정 메커니즘 catch블록). 에 오류가 발생 handleErrorOne하면 다음 catch 블록 (catch(e2) ) 등에서 합니다.

이것은 분명히 당신이 의도 한 것이 아닙니다.

무슨 일이 있어도 전체 해결 체인이 실패하기를 원한다고 가정 해 봅시다.

stepOne()
.then(function(a) {
    return stepTwo(a).then(null, handleErrorTwo);
}, handleErrorOne)
.then(function(b) {
    return stepThree(b).then(null, handleErrorThree);
});

참고 : 거부 handleErrorOne된 경우에만 호출되기 때문에 현재 위치 를 떠날 수 있습니다 stepOne(체인의 첫 번째 기능 이므로이 시점에서 체인이 거부되면 해당 기능의 약속 때문일 수 있음을 알고 있습니다) .

중요한 변화는 다른 함수에 대한 오류 처리기가 주요 약속 체인의 일부가 아니라는 것입니다. 대신, 각 단계에는 자체 “서브 체인”이 있으며onRejected 단계가 거부 된 경우에만 호출되는 있습니다 (하지만 주 체인에 직접 도달 할 수는 없음).

이 작품 이유는 둘이다 onFulfilled하고 onRejected받는 선택적 인수있는 then방법은. 약속이 이행되고 (즉, 해결됨) then체인 의 다음 약속 에 onFulfilled처리기 가없는 경우 해당 처리기가있는 체인이있을 때까지 체인이 계속됩니다.

이는 다음 두 줄이 동일 함을 의미합니다.

stepOne().then(stepTwo, handleErrorOne)
stepOne().then(null, handleErrorOne).then(stepTwo)

그러나 다음 줄은 위의 두 줄과 동일 하지 않습니다 .

stepOne().then(stepTwo).then(null, handleErrorOne)

Angular의 약속 라이브러리 $q는 kriskowal의 Q라이브러리 (더 풍부한 API를 가지고 있지만에서 찾을 수있는 모든 것을 포함 $q)를 기반으로합니다. GitHub 의 Q API 문서 가 유용 할 수 있습니다. Q는 Promises / A + spec을 구현 합니다.then 은 약속 해결 동작이 정확히 작동 합니다.

편집하다:

또한 오류 처리기에서 체인을 벗어나려면 거부 된 약속을 반환하거나 오류 (거부 된 약속에 자동으로 잡히고 포장됩니다)를 throw해야합니다. 약속을 반환하지 않으면 반환 then값을 해결 약속으로 래핑합니다.

즉, 아무 것도 반환하지 않으면 가치에 대한 해결 된 약속을 효과적으로 반환하는 것 undefined입니다.


답변

파티에 늦었지만이 간단한 솔루션이 나를 위해 일했습니다.

function chainError(err) {
  return Promise.reject(err)
};

stepOne()
.then(stepTwo, chainError)
.then(stepThreee, chainError);

이를 통해 체인 에서 벗어날 수 있습니다 .


답변

필요한 것은 .then()특별한 케이스를 시작하고 특별한 케이스를 마무리 하는 반복 체인입니다.

요점은 실패 사례의 단계 번호를 최종 오류 처리기로 리플하는 것입니다.

  • 시작 : step(1)무조건 호출 합니다.
  • 반복 패턴 : .then()다음 콜백으로 체인 a 를 연결하십시오.
    • 성공 : 호출 단계 (n + 1)
    • 실패 : 이전 지연된 값이 거부 된 값을 던지거나 오류를 다시 발생시킵니다.
  • 완료 : .then()성공 처리기와 최종 오류 처리기가없는 체인 a

모든 것을 손으로 쓸 수는 있지만 명명 된 일반 함수를 사용하여 패턴을 쉽게 설명 할 수 있습니다.

function nextStep(n) {
    return step(n + 1);
}

function step(n) {
    console.log('step ' + n);
    var deferred = $q.defer();
    (n === 3) ? deferred.reject(n) : deferred.resolve(n);
    return deferred.promise;
}

function stepError(n) {
    throw(n);
}

function finalError(n) {
    console.log('finalError ' + n);
}
step(1)
    .then(nextStep, stepError)
    .then(nextStep, stepError)
    .then(nextStep, stepError)
    .then(nextStep, stepError)
    .then(nextStep, stepError)
    .then(null, finalError);});

참조 데모

step()에서 지연된 항목이 거부되거나 해결되어 체인 n에서 다음 값의 콜백에 해당 값을 사용할 수있는 방법에 유의하십시오 .then(). stepError이 호출 되면 에 의해 처리 될 때까지 오류가 반복해서 다시 발생합니다 finalError.


답변

거부 할 때 거부 오류를 전달해야하는 경우 체인이 끝날 때까지 거부를 처리하거나 “다시 던져야”하는지를 검사하는 함수에서 단계 오류 핸들러를 랩핑하십시오.

// function mocking steps
function step(i) {
    i++;
    console.log('step', i);
    return q.resolve(i);
}

// function mocking a failing step
function failingStep(i) {
    i++;
    console.log('step '+ i + ' (will fail)');
    var e = new Error('Failed on step ' + i);
    e.step = i;
    return q.reject(e);
}

// error handler
function handleError(e){
    if (error.breakChain) {
        // handleError has already been called on this error
        // (see code bellow)
        log('errorHandler: skip handling');
        return q.reject(error);
    }
    // firs time this error is past to the handler
    console.error('errorHandler: caught error ' + error.message);
    // process the error 
    // ...
    //
    error.breakChain = true;
    return q.reject(error);
}

// run the steps, will fail on step 4
// and not run step 5 and 6
// note that handleError of step 5 will be called
// but since we use that error.breakChain boolean
// no processing will happen and the error will
// continue through the rejection path until done(,)

  step(0) // 1
  .catch(handleError)
  .then(step) // 2
  .catch(handleError)
  .then(step) // 3
  .catch(handleError)
  .then(failingStep)  // 4 fail
  .catch(handleError)
  .then(step) // 5
  .catch(handleError)
  .then(step) // 6
  .catch(handleError)
  .done(function(){
      log('success arguments', arguments);
  }, function (error) {
      log('Done, chain broke at step ' + error.step);
  });

콘솔에 표시되는 내용 :

step 1
step 2
step 3
step 4 (will fail)
errorHandler: caught error 'Failed on step 4'
errorHandler: skip handling
errorHandler: skip handling
Done, chain broke at step 4

다음은 작동 코드입니다
https://jsfiddle.net/8hzg5s7m/3/

각 단계마다 특정 처리가있는 경우 래퍼는 다음과 같습니다.

/*
 * simple wrapper to check if rejection
 * has already been handled
 * @param function real error handler
 */
function createHandler(realHandler) {
    return function(error) {
        if (error.breakChain) {
            return q.reject(error);
        }
        realHandler(error);
        error.breakChain = true;
        return q.reject(error);
    }
}

그럼 네 체인

step1()
.catch(createHandler(handleError1Fn))
.then(step2)
.catch(createHandler(handleError2Fn))
.then(step3)
.catch(createHandler(handleError3Fn))
.done(function(){
    log('success');
}, function (error) {
    log('Done, chain broke at step ' + error.step);
});


답변

올바르게 이해하면 실패한 단계의 오류 만 표시하고 싶습니까?

첫 번째 약속의 실패 사례를 변경하는 것만 큼 간단합니다.

step(1).then(function (response) {
    step(2);
}, function (response) {
    stepError(1);
    return response;
}).then( ... )

$q.reject()첫 번째 단계의 실패 사례 로 돌아 가면 해당 약속을 거부하여 2에서 errorCallback이 호출됩니다 then(...).


답변

var s = 1;
start()
.then(function(){
    return step(s++);
})
.then(function() {
    return step(s++);
})
.then(function() {
    return step(s++);
})
.then(0, function(e){
   console.log(s-1);
});

http://jsbin.com/EpaZIsIp/20/edit

또는 여러 단계에 대해 자동화하십시오.

var promise = start();
var s = 1;
var l = 3;
while(l--) {
    promise = promise.then(function() {
        return step(s++);
    });
}
promise.then(0, function(e){
   console.log(s-1);
});

http://jsbin.com/EpaZIsIp/21/edit


답변

다음과 같이 libs를 사용하십시오.

https://www.npmjs.com/package/promise-chain-break

    db.getData()
.then(pb((data) => {
    if (!data.someCheck()) {
        tellSomeone();

        // All other '.then' calls will be skiped
        return pb.BREAK;
    }
}))
.then(pb(() => {
}))
.then(pb(() => {
}))
.catch((error) => {
    console.error(error);
});