javascript - 更复杂的延迟/ promise 使用和链只是不点击。希望得到一些澄清

标签 javascript node.js asynchronous jquery-deferred promise

我发现各种 Deferred/promise 库的文档通常涵盖非常简单的用例,例如链接 1 或 2 个函数/方法并导致解决或拒绝成功或错误。

但是,当涉及到更复杂的用例(5 个以上函数/方法的链;嵌套的 Deferreds;从其他 Deferreds 内部返回 Promise)时,我会空手而归,并变得非常沮丧。

例如,我有一个包含 2 个嵌套函数的函数。每个子函数都返回一个 promise ,我希望父函数返回子函数的成功/失败结果:

var saveUser = function(user) {
    var saveNewUser = function(user) {
        var deferred = when.defer();

        user.save(function (err) {
            if (err) {
                deferred.reject();
            }
            else {
                // forcing a rejection here for testing purposes
                deferred.reject(6);
            }
        });

        return deferred.promise;
    }

    var supplyUserCollections = function() {
        var deferred = when.defer();

        deferred.reject();

        return deferred.promise;
    }

    return saveNewUser(user).then(supplyUserCollections(), function() {
        console.log('failed to save new user');
    }).then(function(data) {
        console.log('succeeded to do everything');
    }, function() {
        console.log('failed to seed new user collections');
    });
}

这不起作用;奇怪的是,“成功完成所有操作”console.log 会触发,即使我强制拒绝/失败两个子函数。看到 .then 上的第一个参数是成功的案例,我真的不明白为什么会发生这种情况。此外,假设父函数 saveUser 是另一个广泛的 promise 链的一部分,如下所示:

dropExistingCollections(collections).then(saveEntities(albums), function() {
    console.log('failed to drop existing collections');
}).then(saveEntities(movies), function() {
    console.log('failed to save all albums');
}).then(saveEntities(games), function() {
    console.log('failed to save all movies');
}).then(saveUsers(users), function() {
    console.log('failed to save all games');    
}).then(function(data) {
    console.log(data);

    console.log('successfully saved all seed data');

    res.send('database data wiped and re-seeded');
}, function() {
    console.log('failed to save all users');
});

我不太确定如何以可与其余函数链接的方式正确地从 saveUser 返回 promise ,这些函数都是返回已解决/拒绝的 Deferred 的简单函数。

我真的很想更清楚地了解如何处理这些更复杂的 Deferreds/promise 用例。这显然是一个极其复杂的话题,而且我发现的大部分 Material 并没有引起我的共鸣。

最佳答案

始终从同步代码开始。它更容易理解,并且转换为异步代码从来没有那么困难。如果您包含了如何编写同步代码,而不仅仅是一些稍微损坏的异步代码,那么我会更容易地弄清楚您想要代码做什么。

简短回答

您最初的问题可能只是您在无意的地方添加了括号。您应该始终将函数传递给您的then处理程序:

return saveNewUser(user).then(supplyUserCollections /* NOTE: no parenthesis */, function() {
    console.log('failed to save new user');
}).then(function(data) {
    console.log('succeeded to do everything');
}, function() {
    console.log('failed to seed new user collections');
});

这似乎也是您对第二个函数的主要误解。

长答案

想象一下您要编写的代码可能类似于以下内容(如果用户有 saveSync 方法以及 save 方法)。

function saveNewUser(user) {
  var result = user.saveSync();//may throw
  // forcing a throw here for testing purposes
  throw 6;
}

function supplyUserCollections() {
  throw new Error('failed supplyUserCollections');
}

function saveUser(user) {
  try {
    saveNewUser(user);
  } catch (ex) {
    console.log('failed to save new user');
    return;
  }
  try {
    supplyUserCollections();
  } catch (ex) {
    console.log('failed to seed new user collections');
    return;
  }
  console.log('succeeded to do everything');
}

现在我们可以非常简单地转换 saveNewUsersupplyUserCollections:

function saveNewUser(user) {
  var deferred = when.defer();

  user.save(function (err) {
    if (err) {
      deferred.reject();
    }
    else {
      // forcing a rejection here for testing purposes
      deferred.reject(6);
    }
  });

  return deferred.promise;
}

function supplyUserCollections() {
  var deferred = when.defer();

  deferred.reject();

  return deferred.promise;
}

完成此操作后,我们只需要转换更复杂的 saveUser 方法:

function saveUser(user) {
  saveNewUser(user)
    .then(function () {
      //this only happens if `saveNewUser` succeeded, like the bit after
      //a catch block containing a `return`
      return supplyUserCollections()
        .then(function () {
          console.log('succeeded to do everything');
        }, function (ex) {
          console.log('failed to seed new user collections');
        })
    }, function (ex) {
      //this happens when `saveNewUser` failed (just like the catch block)
      console.log('failed to save new user');
    })
}

现在在我看来,同步函数的更可能的版本可能是这样的:

function saveUser(user) {
  saveNewUser(user);
  supplyUserCollections();
  console.log('succeeded to do everything');
}

你可以写成:

function saveUser(user) {
  saveNewUser(user)
    .then(function () {
      //this only happens if `saveNewUser` succeeded
      return supplyUserCollections()
    })
    .then(function () {
      //this happens when both methods succeeded
      console.log('succeeded to do everything');
    })
}

关于javascript - 更复杂的延迟/ promise 使用和链只是不点击。希望得到一些澄清,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17517593/

相关文章:

javascript - 如何获取网页中元素中文本的字体大小

javascript - 如何使用 React-router v4 以编程方式从操作创建器进行导航?

node.js - Angular CLI-ng服务- Node 进程的cpu使用率高

javascript - IndexedDB 请求和事务的计时

javascript - 将 php 数组从 php 文件引入 js 文件中的 javascript 数组

node.js - 从 nodejs 中的现有模型 Sequelize 的 Mirations

jquery - Ajax Jquery PUT 请求不起作用

c# - C# 中是否有类似 ConcurrentQueue 的数据结构,它允许我等待一个空队列,直到添加一个项目?

c# - .NET 中是否可以使用 C# 实现基于事件的异步模式而无需多线程?

javascript - 这2个window.location.href重定向有什么区别? ( Chrome )