2017-01-05 3 views
5

Попытка взаимодействия с JS API, но сбой при выполнении задачи Grunt; Я думаю, что моя логика смущена. Мои шаги:вернуть обещание от функции исполнителя?

  • получение маркеров из файла, проверить их (check_tokens)
  • , если они старые - обновить их (refresh_tokens)
  • вызовов API, чтобы обновить, если не удается - получить новые (authorize_with_api) < - это вопрос
  • из authorize_with_api отклонять с ошибкой или решить с жетонами

в настоящее время задача Grunt сообщает об UnhandledPromiseRejectionWarning и никогда не заканчивается. Если я прокомментирую звонок на authorize_with_api, он автоматически выйдет с ошибкой, и я получу свое самое верхнее сообщение caught error!.

Почему я не могу вернуть обещание из функции исполнителя? Что случилось с моей логикой?

/* 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' кажется неопределенным внутри' 'authorize_with_api, вы имели в виду, чтобы передать его в? Показать вашу задачу глотки, которая вызывает это может быть полезно –

+1

Пожалуйста, не делайте свое собственное обещание. Для решения этой задачи были написаны библиотеки, используйте один из них. Например, bluebird может сделать это за вас. http://bluebirdjs.com/docs/api/promise.promisifyall.html – Tomalak

+1

@Tomalak Можете ли вы объяснить подробнее? ОП использует собственные обещания? –

ответ

8

Возвращаясь из конструктора Promise (или какой-либо функции в нем) не решает обещание:

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. 

Даже если вы не имели отдачу от sdk.refreshTokens обратного вызова, а вместо этого был прямой return authorize_with_api() без обратного вызова, результат все равно не будет привязан.

Чтобы устранить обещание, вы не можете вернуться из своей конструкции, но необходимо явно вызвать одну из указанных обратных вызовов (решительность/отклонять) вместо:

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 

Разрешающая обещание фактически обрабатывает отказ, а поэтому не имеет значения, если authorize_with_api разрешает или отклоняет, состояние будет соответствующим образом распространяться по цепочке.

Мое предложение еще держать return заявление, чтобы сохранить намеченные семантику if ветви кондиционирования раннее возвращение, но код будет работать без него, потому что Promises can only be resolved once и все последующие вызовы в reject/resolve игнорируются.

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 

Примеры:

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

Отличное объяснение! _removes hat_ – montrealist

+0

Рад помочь помощнику :) – nem035

+0

Обратите внимание, что он даже не «возвращает» из функции исполнителя Promise, а из асинхронного обратного вызова где-то внутри этого. – Bergi