我开发了一个基于 Beancount 的账本托管服务 HostedBeans,欢迎大家来了解纯文本复式记账并试用我的服务。

RP 主机免费计划

回想起来 RP 主机已经创建三年多了,一开始是因为初中时我负担不起 Linode 的价格,于是公开出售虚拟主机。后来规模越来越大,自己编写了名为 RootPanel 的面板,最多的时候有三个节点、两百多个付费用户。RP 主机对我的意义非常特殊,它是我坚持最久、用户最多的项目,我也从这个项目赚到了我的第一桶金。

但最近一年我有了稳定的收入以后,我就没有在使用 RP 主机了,也不是很在乎 RP 主机的那一点点收入了。所以从去年 8 月更新 Node.js 版的 RootPanel 之后,RP 主机就没有过什么更新了,有很多 Bug 到现在也没有解决。现在已经出现了一些好用的 PaaS 服务,而且也不是很贵 ,RP 主机已经没有什么独特的优势了。

但另一方面,我依然在继续改进 RootPanel 这个开源项目,我依然看好这个项目,只是不想再维护 RP 主机了,因为 RP 主机的用户们的一些咨询,和订单的处理会花费我很多时间。

于是本来我是想逐步关闭 RP 主机的,但我突然想到其实可以将 RP 主机转变为一个免费服务,这样我对用户就不再负有很大的责任了,我依旧会尽力保证服务的稳定运行,但我可能不会再去回复用户的一些咨询和求助。

所以我的初步计划是,两到三个月后,RootPanel 会更新一个版本,来提供免费版所需要的功能。届时整个 RP 主机都会变成免费的,大部分功能不会受到影响,会有一些比较小的限制,例如不能使用本地的邮件服务直接发送邮件等。

现在的付费用户可以按照本文发布时的余额(不含通过活动赠送的余额)进行退款,现在起也不会再接受新的充值。稍后我会单独发邮件通知所有用户,或者你也可以通过工单(请不要通过其他方式)来联系我,提供你的支付宝帐号。

Promise:抽象的异步任务

我现在才认识到,我在学习 Node.js 上走的最大的弯路就是很晚才开始了解和使用 Promise.

callback 风格的问题

Node.js 和其他主流语言最不同的地方就在于 Node.js 中所有的 IO 操作都是异步的,在传统的 callback 风格的代码中,异步函数和同步函数(非异步函数)是有很大的区别的,在使用时必须加以区分。

异步函数的最后一个参数是一个称作 callback 的函数,这个函数会在异步任务完成后被执行,第一个参数表示是否出现了错误,其余参数是异步任务执行的结果。而同步函数的执行结果作为返回值返回,在出错时会抛出异常。

try
  console.log syncTask input
catch err
  console.error err

asyncTask input, (err, output) ->
  if err
    console.error err
  else
    console.log output

JavaScript 的异常机制在同步的情况下工作良好,但在异步的情况下则需要你在每一个异步函数完成后手动检查错误,因为这些异步函数并不在同一个调用栈上运行,所以不会像异常一样逐级传递。

try
  input2 = syncTask1 input
  console.log syncTask2 input2
catch err
  console.error err

asyncTask1 input, (err, input2) ->
  if err
    console.error err
  else
    asyncTask2 input2, (err, output) ->
      if err
        console.error error err
      else
        console.log output

这种不一致使得我们需要花费额外的精力去关注异步任务和同步任务之间的差别。一个例子是当一个函数由同步改为异步(例如一个函数本来不需要进行 IO, 但后来变得需要了)的时候需要修改所有调用处,简直令人抓狂。

Promise 的解决方案

而 Promise 通过建立抽象的方式,消除了同步函数和异步函数在调用方法、结果返回、异常流程处理上的差别。

对于 Promise 风格的异步函数,我们不需要在调用时传递一个 callback, 而是像调用同步函数一样,只传递真正的参数。

syncTask input
asyncTask input

Promise 风格的函数的结果总是一个值 —— 因为一个函数只能有一个返回值。

我们总是通过函数返回的 Promise 对象来绑定 callback 或捕捉错误。

syncTask(input).then (output) ->
  console.log output
, (err) ->
  console.error err

asyncTask(input).then (output) ->
  console.log output
, (err) ->
  console.error err

在 Promise 风格的函数中,我们可以通过两种方式来产生一个错误:抛出异常或返回一个「rejected 的 Promise」。

task1 = ->
  throw new Error()

task2 = ->
  return Promise.reject new Error()

当出现错误时,错误会被逐级传递,我们只需在最后一步捕捉错误即可。

task1(input).then (input2) ->
  task2 input2
.then (input3) ->
  task3 input3
.then (output) ->
  console.log output
, (err) ->
  console.error err

Promise 的实现

从实现的角度来将,Promise 风格的函数总是返回一个 Promise 的实例,Promise 的实例是对「一项任务」的抽象。

任务可以有三种状态:正在进行(pending)、成功(resolved)、错误(rejected)。任务的结果(成功时)或错误(错误时)会被保存为这个对象的内部状态,外部只能通过 then 来与这项任务的结果进行交互。

当使用 then 来绑定回调函数时,如果这项任务已经完成,则直接使用内部保存的结果来通知回调函数;若这项任务还未完成,则将回调函数放入队列中,等待任务完成再进行通知。

这种设计实际上是一种订阅/通知模型,Promise 对象负责维护订阅关系,而任务和回调函数本身是不存在联系的。

Promise 的第三方拓展

Promises/A+ 标准仅为 Promise 实例规定了 then 方法,在实际应用中,我们会使用一些与 Promises/A+ 兼容但拓展了更多功能的 Promise 实现,例如:

Atom 中文社区!

我最近半年开始变成 Atom 的深度用户,每天用 Atom 完成我的工作,同时也在了解 Atom 的构造,阅读源代码,编写插件。可以说 Atom 的中文用户很少,我希望能够帮助其他的中文用户了解 Atom, 在经过一番权衡之后,最后还是决定自建一个社区。

http://atom-china.org/

目前我已经翻译了几篇官方的博客和手册:

以及我前些天自己尝试编写的一个插件:

Atom 安装包的百度盘镜像:

http://pan.baidu.com/s/1o6r8DOu (相关讨论)

PS: Atom 中文社区现已加入 中文技术社区联盟

精子生于 1995 年,英文 ID jysperm.

订阅推送

通过 Telegram Channel 订阅我的博客日志、产品和项目的动态:

王子亭的博客 @ Telegram


通过邮件订阅订阅我的博客日志、产品和项目的动态(历史邮件):

该博客使用基于  Hexo  的  simpleblock  主题。博客内容使用  CC BY-NC-ND  授权发布。最后生成于 2025-02-10.