Então, basicamente você está perguntando qual é a diferença entre esses dois (onde pestá uma promessa criada a partir de algum código anterior):
return p.then(...).catch(...);
e
return p.catch(...).then(...);
Existem diferenças quando p resolve ou rejeita, mas se essas diferenças importam ou não depende do que o código dentro dos manipuladores .then()ou .catch()faz.
O que acontece quando presolve:
No primeiro esquema, quando presolve, o .then()manipulador é chamado. Se esse .then()manipulador retornar um valor ou outra promessa que eventualmente seja resolvida, o .catch()manipulador será ignorado. Mas, se o .then()manipulador lança ou retorna uma promessa que eventualmente rejeita, então o .catch()manipulador executará para uma rejeição na promessa original p, mas também para um erro que ocorre no .then()manipulador.
No segundo esquema, quando presolve, o .then()manipulador é chamado. Se esse .then()manipulador lança ou retorna uma promessa que eventualmente rejeita, então o .catch()manipulador não pode capturar isso porque está antes dele na cadeia.
Então, essa é a diferença # 1. Se o .catch()manipulador for AFTER, ele também poderá detectar erros dentro do .then()manipulador.
O que acontece quando prejeita:
Agora, no primeiro esquema, se a promessa for prejeitada, o .then()manipulador será ignorado e o .catch()manipulador será chamado conforme o esperado. O que você faz no .catch()manipulador determina o que é retornado como o resultado final. Se você apenas retornar um valor do .catch()manipulador ou retornar uma promessa que eventualmente é resolvida, a cadeia de promessas muda para o estado resolvido porque você "tratou" o erro e retornou normalmente. Se você lançar ou retornar uma promessa rejeitada no .catch()manipulador, a promessa retornada permanecerá rejeitada.
No segundo esquema, se a promessa for prejeitada, o .catch()manipulador será chamado. Se você retornar um valor normal ou uma promessa que eventualmente seja resolvida do .catch()manipulador ("manipulando" assim o erro), a cadeia de promessas muda para o estado resolvido e o .then()manipulador após o .catch()será chamado.
Então essa é a diferença # 2. Se o .catch()manipulador for ANTES, ele pode manipular o erro e permitir que o .then()manipulador ainda seja chamado.
Quando usar qual:
Use o primeiro esquema se desejar apenas um .catch()manipulador que possa detectar erros na promessa original pou no .then()manipulador e uma rejeição de pdeve ignorar o .then()manipulador.
Use o segundo esquema se quiser ser capaz de detectar erros na promessa original pe talvez (dependendo das condições), permitir que a cadeia de promessa continue como resolvida, executando assim o .then()manipulador.
A outra opção
Há uma outra opção para usar os dois retornos de chamada que você pode passar para .then():
p.then(fn1, fn2)
Isso garante que apenas um de fn1ou fn2será chamado. Se presolver, então fn1será chamado. Se prejeitar, então fn2será chamado. Nenhuma mudança de resultado fn1pode fazer com que fn2seja chamado ou vice-versa. Portanto, se você deseja ter certeza absoluta de que apenas um de seus dois manipuladores é chamado, independentemente do que acontece nos próprios manipuladores, você pode usar p.then(fn1, fn2).