从执行函数返回一个承诺?

时间:2017-01-05 15:11:20

标签: javascript promise ecmascript-6 es6-promise

尝试与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);
            } 
        });
    });
}

2 个答案:

答案 0 :(得分:9)

从Promise构造函数(或其中的任何函数)返回不解析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 :(得分:1)

如果满足以下条件,只需写last的else语句:

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);
            } else {
                reject('tokens == undefined && err == undefined');
            }
        });
    });
}