承诺中间件



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

有什么简单的方法可以将中间件放入承诺链中?

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

一个人可以写

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()
  })

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

关于错误:只有在动作返回承诺的情况下,我才考虑错误,而这一承诺拒绝。

这有意义吗?

那是我写的功能:

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)
    }
  })
}

我们可以这样使用。这是正确的方法,还是我错过了有关我的承诺的明显方法?

您要寻找的中间件概念是then(或catch)回调本身。then(和catch)返回 new Promise(我们称其为newP)。如果您提供给then/catch的回调将返回承诺(或更一般的a thtable ),则thenP是回调返回的,这意味着它将得到满足或根据该承诺/当时的承诺拒绝。如果回调返回了不可能的值(例如,不是承诺),则可以通过该值来实现thenP。如果回调引发异常,则 thenP被拒绝,如果有例外。

(如果"当时"一词不熟悉,或者您不清楚"满足"one_answers"解决"的区别,"我在我的帖子中介绍了"我"在我的帖子中介绍了诺言博客。)

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

否,正确的代码是返回then

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

如果是 Just 函数调用您也可以简单地写下:

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

如果doSomethingdoSomethingElse也异步,请确保它们返回承诺。如果不是,则可以使用以上作用,或者您可以将其结合在一起:

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

在评论中,您问:

如果doSomething不返回任何值怎么办?那么doSomethingElse将不会收到get() ...

的结果

诺言的主要概念之一是它们是A Pipeline ,其中管道中的每个步骤都可以潜在地改变通过它的内容。如果您需要管道中的细分市场无法转换分辨率值,那很好,只需将其与下一个段(如果不是异步)结合在一起,或者让该段返回其收到的内容(如果是):

// (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的分辨率。


旁注:请记住,承诺的规则之一是,您要么将承诺退还给呼叫者,要么自己处理拒绝。以上没有排斥处理程序,但是您需要在生产代码中(或者您需要将链的结果归还给有拒绝处理程序的东西)。

这很简单..

如果您编写了中间软件,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;}

当您处于承诺链中时,您不必回报承诺。您只需要在链条内进行异步电话时就退还承诺。

您只能做

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

最新更新