承诺中间件

时间:2017-09-21 11:42:54

标签: javascript node.js promise

最近我不得不写出奇怪的代码。我不确定,也许我正在重新发明轮子。

有没有简单的方法将中间件放入承诺链?

假设我们有一些承诺,例如,可能是axios.get('/users')。 我们想对结果做一些事情,然后再做一些事情。 假设这些操作位于应用程序的不同部分,我们无法将它们组合成一个函数。 更重要的是,其中一些只是同步的商店更新(并且不会返回任何内容),另一些可能会返回承诺。

有人可以写

axios
  .get('/users')
  .then((result) => {
    doSomething(result)
  })
  .then((result) => {
    doSomethingElse(result)
  })

这不起作用,因为第一个then没有返回承诺,因此第二个then将不会收到结果。 正确的代码(对于没有承诺的同步动作)是:

axios
  .get('/users')
  .then((result) => new Promise((resolve, reject) => {
    doSomething(result)
    resolve(result)
  }))
  .then(result) => new Promise((resolve, reject) => {
    doSomethingElse(result)
    resolve(result)
  })
  ...etc...

但我不喜欢这个。我一个人吗?我希望有类似的东西:

axios
  .get('/users')
  .thenMiddleware(doSomething)
  .then((result) => {
    doSomethingElse()
  })

这意味着我们希望以doSomething的结果运行get('/users'),然后,如果没有错误,我们希望再次使用get('/users')的结果调用下一个链函数。 / p>

关于错误:只有在某个操作返回一个承诺时,我才会考虑错误,并且此承诺会拒绝。

有意义吗?

这就是我写的功能:

const promiseMiddleware = (target) => function(...args) {
  return new Promise((resolve, reject) => {
    if (typeof target === 'function') {
      target = target.apply(this, args)
    }
    if (typeof target === 'object' && typeof target.then === 'function') {
      // Middleware does not count the result of inner promise, it triggers the promise chain to continue with the incomming data instead.
      // But if the inner promise rejects, middleware returns an inner error.
      target.then((targetResult) => {
        // So we don't use targetResult here, we just resolve with the incomming args.
        resolve.apply(this, args)
      }).catch(reject)
    } else {
      resolve.apply(this, args)
    }
  })
}

我们可以像this一样使用它。这是正确的方法,还是我错过了一些可以在我的案例中使用的承诺?

3 个答案:

答案 0 :(得分:5)

您正在寻找的中间件概念是then(或catch)回调本身。 then(和catch)返回承诺(让我们称之为newP)。如果您向then / catch提供的回调返回一个承诺,则thenP将从属于回调返回的承诺。如果回调返回不可用的值(例如,不是保证),则使用该值解析thenP。如果回调引发异常,则thenP将被拒绝该异常。

  

正确的代码(对于没有承诺的同步动作)是

不,正确的代码是退出then

axios
  .get('/users')
  .then(result => {
    return doSomething(result);
  })
  .then(result => {
    return doSomethingElse(result);
  });

如果它是只是函数调用,您也可以简单地编写它:

axios
  .get('/users')
  .then(doSomething)
  .then(doSomethingElse);

如果doSomethingdoSomethingElse也是异步的,请确保它们返回承诺。如果不是,那么上述工作,或者你可以把它结合起来:

// Assumes `doSomething` is not asynchronous
axios
  .get('/users')
  .then(result => doSomethingElse(doSomething(result)));

在评论中你问过:

  

如果doSomething没有返回任何值,该怎么办?然后doSomethingElse将无法收到get() ...

的结果

承诺的主要概念之一是它们是管道,其中管道中的每个步骤都可能改变通过它的内容。如果您需要管道中不会转换分辨率值的段,那很好,只需将它与下一个段组合(如果它不是异步的)或让该段返回它接收的内容(如果是):

// (1) If `doSomething` ISN'T asynchronous or it is but you don't care
// that `doSomething` and `doSomethingElse` run in parallel if it is
axios
  .get('/users')
  .then(result => {
    doSomething(result);
    return doSomethingElse(result);
  });

// (2.1) If `doSomething` IS asynchronous and you want to wait for it but you
// don't want its result
axios
  .get('/users')
  .then(result => {
    return doSomething(result).then(result);
  })
  .then(result =>
    return doSomethingElse(result);
  });

后者也可以写成:

// (2.2) Also if `doSomething` IS asynchronous and you want to wait for it but
// you don't want its result
axios
  .get('/users')
  .then(result => doSomething(result).then(result))
  .then(doSomethingElse);

请注意,(2.1)和(2.2)仍将尊重doSomething的拒绝。如果你想完全忽略其中发生的事情,那么:

// (3) If `doSomething` IS asynchronous and you want to wait for it but
// you don't want its result AND you want to ignore rejections
axios
  .get('/users')
  .then(result => doSomething(result).then(result, result))
  // Change is here -----------------------------^^^^^^^^
  .then(doSomethingElse);

将拒绝转换为result的解析。

附注:请记住,承诺的一个规则是您要么将承诺退还给您的来电者,要么您自己处理拒绝。上面没有拒绝处理程序,但是你需要在生产代码中使用它们(或者你需要将链的结果返回给具有拒绝处理程序的东西)。

答案 1 :(得分:1)

这很简单..

如果你编写了中间件doSomething函数,那么只需让它返回结果而不改变它。如;

function doSomething(result){
  // here i do tons of things with the result but i don't mutate it
  return result;
}

然后你的代码看起来像;

axios
  .get('/users')
  .then(doSomething)     // this will pass result to the next then
  .then(doSomethingElse) // stage automatically wrapped in a promise

顺便说一句,您不需要像.then(res => {doStg(res)})这样做.then(doStg)

但是,如果您对中间件doSomething功能没有任何控制权,那么您所要做的就是;

axios
  .get('/users')
  .then(res => (doSomething(res), res)) // this will pass result to the next then
  .then(doSomethingElse)                // stage automatically wrapped in a promise

注意:如果内部只有几条说明,请使用箭头功能。我不喜欢带箭头的大括号,而是喜欢逗号运算符。所以;

res => (doSomething(res), res)

与;

基本相同
res => {doSomething(res);
        return res;}

答案 2 :(得分:0)

当你在Promise链中时,你不必返回Promise。如果您在链中进行异步调用,则只需返回Promise。

你可以做到

axios
  .get('/users')
  .then((result) => {
    return doSomething(result);
  })
  .then((result) => {
    return doSomethingElse(result);
  })

假设您有一个异步回调样式调用,那么您应该这样做

axios
  .get('/users')
  .then((result) => {
    return new Promise((resolve, reject) => {
      doSomething(result, (err, result) => {
         return err ? reject(err) : resolve(result);
      });
    });
  })
  .then((result) => {
    return doSomethingElse(result);
  })