2017-01-05 39 views
5

Próba interakcji z interfejsem API JS, ale kończy się niepowodzeniem po uruchomieniu przez zadanie Grunt; Myślę, że moja logika jest zdezorientowana. Moje kroki:zwraca obietnicę z funkcji executora?

  • get tokeny z pliku, sprawdzić je (check_tokens)
  • jeśli są stare - odświeżyć je (refresh_tokens)
  • wywołanie API, aby odświeżyć, jeśli się nie powiedzie - dostać nowe (authorize_with_api) < - jest to kwestia
  • z authorize_with_api odrzucić z powodu błędu lub rozwiązać z tokenów

Obecnie zadanie Grunt zgłasza UnhandledPromiseRejectionWarning i nigdy się nie kończy. Jeśli skomentuję połączenie z numerem authorize_with_api, zostanie ono poprawnie zakończone z błędem i wydrukowany zostanie mój najwyższy komunikat .

Dlaczego nie mogę zwrócić obietnicy z funkcji executora? Co jest nie tak z moją logiką?

/* global sdk, config, tokens */ 
return getTokens().then((p_tokens) => { 
    tokens = p_tokens; 
    return check_tokens(tokens); 
}).then((tokens) => { 
    console.log('then() is called!'); 
}).catch((err) => { 
    console.error('caught error!', err); 
}); 

function check_tokens(tokens) { 
    if(are_old(tokens)) { // returns true 
     return refresh_tokens(tokens); 
    } 
    return Promise.resolve(tokens); 
} 

function refresh_tokens(tokens) { 
    return new Promise(function(resolve, reject) { 
     sdk.refreshTokens(tokens.refresh_token, function(err, new_tokens) { 
      if(err) { 
       if(error.code === 'invalid_grant') { 
        return authorize_with_api(); 
       } 
       reject('refreshTokens failed'); 
      } else if(newTokens) { 
       resolve(new_tokens); 
      } 
     }); 
    }); 
} 

function authorize_with_api() { 
    return new Promise(function(resolve, reject) { 
     sdk.getTokens(config.auth_code, function(err, tokens) { 
      if(err) { 
       reject('getTokens failed'); 
      } else if(tokens) { 
       resolve(tokens); 
      } 
     }); 
    }); 
} 
+0

'tokens' wydaje się być zdefiniowana wewnątrz' 'authorize_with_api, miałaś na myśli, aby przekazać go w? Pokazanie Twojego zadania, które to nazywa, może być pomocne. –

+1

Należy unikać tworzenia własnych promisyfikacji. Biblioteki zostały napisane, aby rozwiązać to zadanie, użyj jednego z nich. Na przykład bluebird może to dla ciebie zrobić. http://bluebirdjs.com/docs/api/promise.promisifyall.html – Tomalak

+1

@ Tomalak Czy możesz wyjaśnić dalej? OP stosuje natywne obietnice? –

Odpowiedz

8

Wracając z konstruktora Obietnica (lub dowolnej funkcji w nim) nie rozwiąże obietnicę:

return new Promise(function(resolve, reject) { 
    sdk.refreshTokens(..., function(err, new_tokens) { 
    if(error.code === 'invalid_grant') { 
     return authorize_with_api(); 
    } // ^--- this will not chain to the promise being created. 

Nawet jeśli nie ma powrotu z sdk.refreshTokens zwrotnego i zamiast miał bezpośredni return authorize_with_api() bez wywołania zwrotnego, wynik nadal nie zostałby powiązany.

Aby rozwiązać obietnicę, że nie może wrócić z jego konstrukcji, ale musi wyraźnie zadzwonić pod jeden z podanych wywołań zwrotnych (postanowienie/odrzucania) zamiast:

return new Promise(function(resolve, reject) { 
    sdk.refreshTokens(..., function(err, new_tokens) { 
    if(error.code === 'invalid_grant') { 
     resolve(authorize_with_api()); 
    } // ^--- must call resolve here 

Rozwiązywanie obietnica rzeczywiście obsługuje odrzucenia, jak również tak bez względu na to czy authorize_with_api rozwiązuje lub odrzuca, stan będzie odpowiednio propagował w górę łańcucha.

Moja propozycja jest nadal zachować oświadczenie return utrzymanie zamierzonego semantykę klimatyzacji if oddziału wczesny powrót ale kod będzie działać bez niego, ponieważ Promises can only be resolved once i wszystkie dalsze rozmowy do reject/resolve są ignorowane.

return new Promise(function(resolve, reject) { 
    sdk.refreshTokens(..., function(err, new_tokens) { 
    if(error.code === 'invalid_grant') { 
     return resolve(authorize_with_api()); 
    } // ^--- should still return here for readability - clean logic purposes 
    reject('refreshTokens failed'); // this will be ignored if the above `resolve` gets called first, no matter if you have the `return` statement 

Przykłady:

function success() { 
 
    return Promise.resolve('success'); 
 
} 
 

 
function error() { 
 
    return Promise.reject('error'); 
 
} 
 

 
function alwaysPending() { 
 
    return new Promise(() => { 
 
    return success(); 
 
    }); 
 
} 
 

 
function resolves() { 
 
    return new Promise((resolve) => { 
 
    resolve(success()); 
 
    }); 
 
} 
 

 
function rejects() { 
 
    return new Promise((resolve) => { 
 
    resolve(error()); 
 
    }); 
 
} 
 

 
alwaysPending().then(console.log); // doesn't log anything 
 
resolves().then(console.log); 
 
rejects().catch(console.log);

+1

Doskonałe wyjaśnienie! _removes hat_ – montrealist

+0

Cieszę się, że mogę pomóc partnerowi :) – nem035

+0

Zauważ, że on nawet nie "zwraca" z funkcji wykonawczej 'Promise', ale z asynchronicznego wywołania zwrotnego gdzieś w tym miejscu. – Bergi