【问题标题】:How to use confirm alert and return an AJAX promise?如何使用确认警报并返回 AJAX 承诺?
【发布时间】:2015-12-24 13:39:09
【问题描述】:

我有一个返回 AJAX 承诺的现有函数。我想更新此函数以在运行 AJAX 调用之前显示确认警报,但代码的其他部分使用此函数并且已经在期待一个承诺。

这是我原来的函数的样子:

function doTheDeed() {
  return $.ajax({
    url: '/api/delete/123',
    method: 'POST'
  }).done(function () {
    alert('The deed is done.');
  });
}

doTheDeed().done(function {} { /*Do something else*/ });

现在我想在运行 AJAX 调用之前与用户确认。在等待用户确认或取消的同时,如何维护返回 Promise 的 API 协议?

function doTheDeed() {
  bootbox.confirm('Are you sure?', function (result) {
    if (result) {
      // Too late to return a promise, promise should've been returned a long time ago
      return $.ajax({
        url: '/api/delete/123',
        method: 'POST'
      }).done(function () {
        alert('The deed is done.');
      });
    } else {
      //return what??
    }
  });
}

doTheDeed().done(function {} { /*Do something else*/ });

我是否需要根据用户的响应有条件地返回不同的承诺?

【问题讨论】:

  • 当你使用 jquery 时,deferred object 应该可以做你想做的事
  • bootbox.confirm 调用的结果做出承诺(这似乎也是异步的,你不能只在回调中使用return)。然后将它与 ajax 调用一起chain

标签: javascript jquery ajax promise


【解决方案1】:

你可以使用 jQuery 的 Deferreds 吗?

function doTheDeed() {
    var def = $.Deferred();

    bootbox.confirm('Are you sure?', def.resolve);

    return def.promise().then(function(result) {
        return result ? $.post('/api/delete/123') : "no go";
    });
}

doTheDeed().done(function (data) { 
    if (data == 'no go') {
        // user declined
    } else {
        // item deleted
    }
}).fail(function(err) {
    // something failed
});

【讨论】:

  • 请只承诺bootbox.confirm,然后避免deferred antipattern
  • @Bergi - 这不需要实际更改 Bootbox,或者使用具有 promisify 方法的东西,例如 Bluebird
  • 不,它可以通过单独的方法轻松完成(修改bootbox 对象本身是可能的,但不是必需的),甚至可以直接在doTheDeed 中完成。我更专注于您将 deferred 专门用于确认,仅此而已。然后使用then链接ajax调用。
  • 是的,完全正确。看起来比您答案中的当前代码要好得多,不是吗? (并且还能正确处理 ajax 失败案例)
  • 谢谢伙计们,这很优雅!
【解决方案2】:

基于 Adeneo 的回答,并坚持最低级别的承诺原则,可以使用 bootbox.confirm() 的可重用承诺对 bootbox 进行猴子补丁,而无需侵入 bootbox 本身。

如果你这样做,那将是合适的:

  • 允许调用者指定质询文本和 noGo 消息。
  • 将 noGo 条件发送到错误路径。
if(!bootbox.confirmAsync) {
    bootbox.confirmAsync = function (challenge, noGoMessage) {
        challenge = challenge || 'Are you sure?';
        noGoMessage = noGoMessage || 'User declined';
        return jQuery.Deferred(function (def) {
            bootbox.confirm(challenge, function (confirmed) {
                confirmed ? def.resolve() : def.reject(new Error(noGoMessage));
            });
        }).promise();
    }
}

doTheDeed() 将包含一个以bootbox.confirmAsync(...) 开头的完全传统的 then 链,例如:

function doTheDeed () {
    return bootbox.confirmAsync('Are you sure you want to delete?', 'User declined to delete')
    .then(function (data) {
        return $.post('/api/delete/123');
    })
    .then(null, function (err) {
        // if user declined, then 'User declined to delete' will come back as err.message
        console.log(err.message);
        return err; // jQuery v<3.0
        // throw err; // jQuery v>=3.0
    });
}

【讨论】:

  • 我会避免在 jQuery "catch" 处理程序中使用 return err。即使你知道它的作用,你也知道当切换到合适的 Promise 库时它不会再工作了。
  • 我们在这里非常致力于 jQuery。 Bootbox 依赖它作为实现 AJAX 的手段。如果 OP 要远离 jQuery,很多事情都需要改变,而不仅仅是 return err
  • 是的,bootbox.confirm$.post 很可能依赖于 jQuery,但这并不能阻止您使用不同的 Promise 库。
  • Bergi,我不确定我是否明白你的意思。如果 doTheDeed 的调用者想用Promise.resolve(doTheDeed()) 同化返回的 jQuery 承诺,则可以这样做。在doTheDeed() 中,所有内容都保证是jQuery 并且return err 是正确的——它只不过是jQuery 的throw err 等价物。我错过了什么?
  • 我已添加评论。
猜你喜欢
  • 2020-07-11
  • 2013-11-15
  • 1970-01-01
  • 2018-12-12
  • 1970-01-01
  • 1970-01-01
  • 2016-06-15
  • 2018-10-19
  • 1970-01-01
相关资源
最近更新 更多