javascript - 从执行函数返回 promise ?

标签 javascript promise ecmascript-6 es6-promise

尝试与 JS API 交互,但在由 Grunt 任务运行时失败;我觉得我的逻辑很困惑。我的步骤:

  • 从文件中获取 token ,检查它们 (check_tokens)
  • 如果它们是旧的 - 刷新它们(refresh_tokens)
  • 调用 API 刷新,如果失败 - 获取新的 (authorize_with_api) <- 这就是问题所在
  • 来自 authorize_with_api 错误拒绝或使用 token 解决

目前 Grunt 任务报告一个 UnhandledPromiseRejectionWarning 并且永远不会完成。如果我注释掉对 authorize_with_api 的调用,它会正确退出并出现错误,并且我会打印最上面的 caught error! 消息。

为什么我不能从执行函数中返回一个 promise ?我的逻辑有什么问题?

/* 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);
            } 
        });
    });
}

最佳答案

从 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(),结果仍然不会被束缚。

要解决 promise ,您不能从其构造函数返回,而必须显式调用给定回调之一(解决/拒绝):

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

解决 promise 实际上也会处理拒绝,因此无论 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);

关于javascript - 从执行函数返回 promise ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41488363/

相关文章:

javascript - 通过键名而不是索引获取数组中的对象

javascript - meteor 登录电子邮件域预定义

javascript - 我怎样才能避免在我的案例中使用另一个 promise ?

javascript - 导出默认值和导出{默认}之间的区别

javascript - 解构 map

javascript - NUXT 警告 "Emitted value instead of an instance of Error"是关于什么的?

javascript - 无法以 Angular 2 绑定(bind)数据

javascript - 在我返回 res.end() 之后,Promise 仍然会转到下一个链

javascript - 全局监控未兑现的 promise 以显示指标

javascript - js如何破坏函数的默认值?