【问题标题】:How to transform an imperative Promise to a functional Task?如何将命令式 Promise 转换为功能性任务?
【发布时间】:2020-09-23 20:45:14
【问题描述】:

我想以有原则的方式将命令式 Promise 转换为函数式 Task

const record = (type, o) =>
  (o[type.name || type] = type.name || type, o);

const thisify = f => f({});

const taskFromPromise = p =>
  Task((res, rej) =>
    p.then(res)
      .catch(x => rej(`Error: ${x}`)));

const Task = task => record(
  Task,
  thisify(o => {
    o.task = (res, rej) =>
      task(x => {
        o.task = k => k(x);
        return res(x);
      }, rej);
    
    return o;
  }));

const tx = taskFromPromise(Promise.resolve(123)),
  ty = taskFromPromise(Promise.reject("reason").catch(x => x));
//                                              ^^^^^ necessary to avoid uncaught error
  
tx.task(console.log); // 123
ty.task(console.log); // "reason" but should be "Error: reason"

解决方案有效,但拒绝无效,因为Promises 被急切地触发。如果我放弃了catch 处理程序,我将不得不将整个计算放入try/catch 语句中。有没有更可行的选择?

【问题讨论】:

  • 您的 task 方法需要两个参数,但您只传递了一个参数。
  • 我看不出你的Task 比承诺“更不重要”或“更实用”。如果您可以在它们之间来回转换,那么它们在大多数情况下都是相等的。预期的区别是什么?
  • Promise 立即调用传递的函数,而 Task 创建一种函数组合形式。两者都是根本不同的操作,我不会列出后果,因为您肯定知道它们。也许你认为Task 是必要的,因为它会导致本地突变以启用共享。我还不确定多播是否是有害的或可取的属性。答案可能取决于您是否需要取消。
  • 是的,您的 Task 只是一个 Promise 具有延迟初始执行,而不是一个函数。正是这种突变和异步效果的结果共享breaks the monad properties for promises 使它们成为必不可少的工具。 (你的分享也被破坏了,因为它不会在承诺完成之前发生,也不会考虑拒绝)。
  • 运行 Task 是 Javascript 的运行时。运行时没有 RT,因为所有效果都被释放。不分享拒绝案例是一个设计决策。 你的分享也被破坏了,因为它不会在承诺实现之前发生 - 这是一个有偏见的陈述。 Tasks 不是承诺。共享仅适用于有限的用例。

标签: javascript asynchronous ecmascript-6 functional-programming


【解决方案1】:

您不需要.catch(x => x),它会将您拒绝的承诺变成已解决的承诺。您不应该收到“未捕获的错误”,因为您的 catch 函数中有一个 catch。删除 .catch(x => x) 确实有效,而是让您进入 .catch(x => rej(`Error: ${x}`))

但是删除.catch(x => x) 确实会抛出“TypeError: rej is not a function”。从您的代码示例看来,您的 .task(...)(在 tx.task(...)ty.task(...) 中)需要两个函数。如果 promise 解决,则调用第一个,如果 promise 被拒绝,则调用第二个。提供这两个函数给我留下了一个工作代码 sn-p。

const record = (type, o) =>
  (o[type.name || type] = type.name || type, o);

const thisify = f => f({});

const taskFromPromise = p =>
  Task((res, rej) =>
    p.then(res)
      .catch(x => rej(`Error: ${x}`)));

const Task = task => record(
  Task,
  thisify(o => {
    o.task = (res, rej) => // expects two functions
      task(x => {
        o.task = k => k(x);
        return res(x);
      }, rej);
    
    return o;
  }));

const tx = taskFromPromise(Promise.resolve(123)),
  ty = taskFromPromise(Promise.reject("reason")); // removed .catch(x => x)

tx.task(console.log, console.log); // provide two functions
ty.task(console.log, console.log);
//         ^            ^ 
//      if resolved  if rejected

【讨论】:

  • 我对@9​​87654331@s 真的一无所知。如果没有直接附加捕获处理程序,我认为被拒绝的承诺会立即抛出。很抱歉这个菜鸟问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-03-16
  • 1970-01-01
  • 1970-01-01
  • 2018-04-02
  • 2022-01-07
  • 2012-07-19
  • 2021-10-08
相关资源
最近更新 更多