诺言比CPS和持续函数/单子的优势是什么?



es6承诺

ES6承诺是有限的状态机,因此需要复杂的实现。除此之外

  • 超载then(地图/链)
  • 递归扁平/当时的同化
  • 自动提升
  • 几个订户(多播)
  • 渴望评估

多播分布和渴望评估是ES6承诺无法取消的原因。此外,我们不能添加自己的当时具有特定功能的当时的物品层,因为它们会立即被递归扁平化所吸收。

我很确定这些设计决策中的任何一个都有很多充分的理由。但是,现在我们拥有一项不变的核心语言功能,而不是特定的,竞争性的DSL用于Userland的异步控制流。当然,Interop很重要,但是能够发展异步控制流的能力而不必考虑整个语言的后退兼容性。

延续通过样式

延续传递样式从异步控制流中抽象,因为它摆脱了return语句。为了恢复合成性,我们仅在连续性的背景下需要一个函子:

const compk = (f, g) => x => k => f(x) (x => g(x) (k));
const inck = x => k => setTimeout(k, 0, x + 1);
const log = prefix => x => console.log(prefix, x);
compk(inck, inck) (0) (log("async composition:")); // 2

当然,我们要构成两个以上的功能。需要提供程序化解决方案,而不是手动编写compk3 = (f, g, h) => x => k => f(x) (x => g(x) (y => h(y) (k)))等:

const compkn = (...fs) => k => 
 fs.reduceRight((chain, f) => x => f(x) (chain), k);
const inck = x => (res, rej) => setTimeout(res, 0, x + 1);
const log = prefix => x => console.log(prefix, x);
compkn(inck, inck, inck) (log("async composing n functions:")) (0); // 3

这种方法完全没有例外处理。让我们天真地调整常见的回调模式:

const compk = (f, g) => x => (res, rej) =>
 f(x) (x => g(x) (res), x => rej(x));
const compkn = (...fs) => (res, rej) =>
 fs.reduceRight((chain, f) => x => f(x) (chain, x => rej(x)), res);
const inc = x => x + 1;
const lift = f => x => k => k(f(x));
const inck = x => (res, rej) => setTimeout(res, 0, x + 1);
const decUIntk = x => (res, rej) =>
 setTimeout(x => x < 0 ? rej("out of range " + x) : res(x), 0, x - 1);
const log = prefix => x => console.log(prefix, x);
compk(decUIntk, inck) (0)
 (log("resolved with:"), log("rejected with:")); // rejected
compkn(inck, decUIntk, inck)
 (log("resolved with:"), log("rejected with:")) (0); // resolved

这只是一个草图 - 必须花费大量精力来实现适当的解决方案。但这是我想的概念证明。 compk/ compkn非常简单,因为它们不必战斗状态。

那么,复杂ES6的优势在持续传递样式和相应的DSL(如Continuation functor/Monad)中有什么优点?

依赖功能组成的任何方法的缺点是,惯用的JavaScript代码序列被命名函数列表代替。承诺自己会遭受这一困扰。

例如。我看到人们做我所谓的回调lite

let foo = () => Promise.resolve().then(() => console.log('foo'));
let bar = () => Promise.resolve().then(() => console.log('bar'));
foo().then(bar);

这是一种方法,但不是唯一的方法,我个人不喜欢它,就像我不喜欢用英语或动作列表替换JavaScript的任何尝试一样。

对我来说,承诺的好处是,我们可以完全避免传统回调的间接,并按顺序编写代码,朝着向前的方向进行。箭头功能帮助:

Promise.resolve('foo')
  .then(foo => {
    console.log(foo);
    return Promise.resolve('bar');
  })
  .then(bar => {
    console.log(bar);
  });

但是,这可以说仍然是一个动作列表。

对我来说,ES6承诺的最大优势是它们与async/await的兼容性,这让我们为异步代码编写惯用的Javascript很像我们会像我们同步代码一样,尽管不是来自顶级范围(但需要Chrome或FireFox Beta):

(async () => {
  console.log(await Promise.resolve('foo'));
  console.log(await Promise.resolve('bar'));
})();

相关内容

  • 没有找到相关文章