博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
More than React(五)异步编程真的好吗?
阅读量:6681 次
发布时间:2019-06-25

本文共 7620 字,大约阅读时间需要 25 分钟。

More than React系列的上一篇文章介绍了 Binding.scala 如何在渲染 HTML 时静态检查语法错误和语义错误,从而避免 bug ,写出更健壮的代码。本篇文章将讨论Binding.scala和其他前端框架如何向服务器发送请求并在页面显示。

\\

在过去的前端开发中,向服务器请求数据需要使用异步编程技术。异步编程的概念很简单,指在进行 I/O 操作时,不阻塞当前执行流,而通过回调函数处理 I/O 的结果。不幸的是,这个概念虽然简单,但用起来很麻烦,如果错用会导致 bug 丛生,就算小心翼翼的处理各种异步事件,也会导致程序变得复杂、更难维护。

\\

Binding.scala 可以用 I/O 状态的绑定代替异步编程,从而让程序又简单又好读,对业务人员也更友好。

\\

我将以一个从 Github 加载头像的 DEMO 页面为例,说明为什么异步编程会导致代码变复杂,以及 Binding.scala 如何解决这个问题。

\\

一、DEMO 功能需求

\\

作为 DEMO 使用者,打开页面后会看到一个文本框。

\\

在文本框中输入任意 Github 用户名,在文本框下方就会显示用户名对应的头像,如下图所示。

\\

4f54f2c3f9628de97013d107cbf23406.png

\\

要想实现这个需求,可以用 Github API 发送的 HTTPS 请求。

\\

发送请求并渲染头像的完整流程的验收标准如下:

\\
  • 如果用户名为空,显示“请输入用户名”的提示文字;\\t
  • \\t

    如果用户名非空,发起 Github API,并根据 API 结果显示不同的内容:

    \\\t
    • 如果尚未加载完,显示“正在加载”的提示信息;\\t\t
    • 如果成功加载,把回应解析成 JSON,从中提取头像 URL 并显示;\\t\t
    • 如果加载时出错,显示错误信息。\\t
    \

二、异步编程和 MVVM

\\

过去,我们在前端开发中,会用异步编程来发送请求、获取数据。比如 ECMAScript 2015 的 Promise 和 HTML 5 的 fetch API。

\\

而要想把这些数据渲染到网页上,我们过去的做法是用 MVVM 框架。在获取数据的过程中持续修改 View Model ,然后编写 View 把 View Model 渲染到页面上。这样一来,页面上就可以反映出加载过程的动态信息了。比如,ReactJS 的 state 就是 View Model,而 render 则是 View ,负责把 View Model 渲染到页面上。

\\

用 ReactJS 和 Promise 的实现如下:

\\
\class Page extends React.Component {\  state = {\    githubUserName: null,\    isLoading: false,\    error: null,\    avatarUrl: null,\  };\  currentPromise = null;\  sendRequest(githubUserName) {\    const currentPromise = fetch(`https://api.github.com/users/${githubUserName}`);\    this.currentPromise = currentPromise;\    currentPromise.then(response =\u0026gt; {\      if (this.currentPromise != currentPromise) {\        return;\      }\      if (response.status \u0026gt;= 200 \u0026amp;\u0026amp; response.status \u0026lt; 300) {\        return response.json();\      } else {\        this.currentPromise = null;\        this.setState({\          isLoading: false,\          error: response.statusText\        });\      }\    }).then(json =\u0026gt; {\      if (this.currentPromise != currentPromise) {\        return;\      }\      this.currentPromise = null;\      this.setState({\        isLoading: false,\        avatarUrl: json.avatar_url,\        error: null\      });\    }).catch(error =\u0026gt; {\      if (this.currentPromise != currentPromise) {\        return;\      }\      this.currentPromise = null;\      this.setState({\        isLoading: false,\        error: error,\        avatarUrl: null\      });\    });\    this.setState({\      githubUserName: githubUserName,\      isLoading: true,\      error: null,\      avatarUrl: null\    });\  }\  changeHandler = event =\u0026gt; {\    const githubUserName = event.currentTarget.value;\    if (githubUserName) {\      this.sendRequest(githubUserName);\    } else {\      this.setState({\        githubUserName: githubUserName,\        isLoading: false,\        error: null,\        avatarUrl: null\      });\    }\  };\  render() {\    return (\      \u0026lt;div\u0026gt;\        \u0026lt;input type=\"text\" onChange={this.changeHandler}/\u0026gt;\        \u0026lt;hr/\u0026gt;\        \u0026lt;div\u0026gt;\          {\            (() =\u0026gt; {\              if (this.state.githubUserName) {\                if (this.state.isLoading) {\                  return \u0026lt;div\u0026gt;{`Loading the avatar for ${this.state.githubUserName}`}\u0026lt;/div\u0026gt;\                } else {\                  const error = this.state.error;\                  if (error) {\                    return \u0026lt;div\u0026gt;{error.toString()}\u0026lt;/div\u0026gt;;\                  } else {\                    return \u0026lt;img src={this.state.avatarUrl}/\u0026gt;;\                  }\                }\              } else {\                return \u0026lt;div\u0026gt;Please input your Github user name\u0026lt;/div\u0026gt;;\              }\            })()\          }\        \u0026lt;/div\u0026gt;\      \u0026lt;/div\u0026gt;\    );\  }\}
\\

一共用了 100 行代码。

\\

由于整套流程由若干个闭包构成,设置、访问状态的代码五零四散,所以调试起来很麻烦,我花了两个晚上才调通这 100 行代码。

\\

三、Binding.scala

\\

现在我们有了 Binding.scala ,由于 Binding.scala 支持自动远程数据绑定,可以这样写:

\\
\@dom def render = {\  val githubUserName = Var(\"\")\  def inputHandler = { event: Event =\u0026gt; githubUserName := event.currentTarget.asInstanceOf[Input].value }\  \u0026lt;div\u0026gt;\    \u0026lt;input type=\"text\" oninput={ inputHandler }/\u0026gt;\    \u0026lt;hr/\u0026gt;\    {\      val name = githubUserName.bind\      if (name == \"\") {\        \u0026lt;div\u0026gt;Please input your Github user name\u0026lt;/div\u0026gt;\      } else {\        val githubResult = FutureBinding(Ajax.get(s\"https://api.github.com/users/${name}\"))\        githubResult.bind match {\          case None =\u0026gt;\            \u0026lt;div\u0026gt;Loading the avatar for { name }\u0026lt;/div\u0026gt;\          case Some(Success(response)) =\u0026gt;\            val json = JSON.parse(response.responseText)\            \u0026lt;img src={ json.avatar_url.toString }/\u0026gt;\          case Some(Failure(exception)) =\u0026gt;\            \u0026lt;div\u0026gt;{ exception.toString }\u0026lt;/div\u0026gt;\        }\      }\    }\  \u0026lt;/div\u0026gt;\}
\\

一共 25 行代码。

\\

完整的 DEMO 请访问 。

\\

之所以这么简单,是因为 Binding.scala 可以用 把 API 请求当成普通的绑定表达式使用,表示 API 请求的当前状态。

\\

每个 FutureBinding 的状态有三种可能,None表示操作正在进行,Some(Success(...))表示操作成功,Some(Failure(...))表示操作失败。

\\

还记得绑定表达式的 .bind 吗?它表示“each time it changes”。

\由于 FutureBinding 也是 的子类型,所以我们就可以利用 .bind ,表达出“每当远端数据的状态改变”的语义。

\\

结果就是,用 Binding.scala 时,我们编写的每一行代码都可以对应验收标准中的一句话,描述着业务规格,而非“异步流程”这样的技术细节。

\\

让我们回顾一下验收标准,看看和源代码是怎么一一对应的:

\\
  • 如果用户名为空,显示“请输入用户名”的提示文字;\
\if (name == \"\") {\\u0026lt;div\u0026gt;Please input your Github user name\u0026lt;/div\u0026gt;
\\
  • 如果用户名非空,发起 Github API,并根据 API 结果显示不同的内容:\
\} else {\val githubResult = FutureBinding(Ajax.get(s\"https://api.github.com/users/${name}\"))\githubResult.bind match {
\\
  • 如果尚未加载完,显示“正在加载”的提示信息;\
\case None =\u0026gt;\  \u0026lt;div\u0026gt;Loading the avatar for { name }\u0026lt;/div\u0026gt;
\\
  • 如果成功加载,把回应解析成 JSON,从中提取头像 URL 并显示;\
\case Some(Success(response)) =\u0026gt;\  val json = JSON.parse(response.responseText)\  \u0026lt;img src={ json.avatar_url.toString }/\u0026gt;
\\
  • 如果加载时出错,显示错误信息。\
\case Some(Failure(exception)) =\u0026gt; // 如果加载时出错,\  \u0026lt;div\u0026gt;{ exception.toString }\u0026lt;/div\u0026gt; // 显示错误信息。
\\

四、结论

\\

本文对比了 ECMAScript 2015 的异步编程和 Binding.scala 的 FutureBinding 两种通信技术。Binding.scala 概念更少,功能更强,对业务更为友好。

\\
技术栈 ReactJS + Promise + fetch Binding.scala
编程范式 MVVM + 异步编程 远程数据绑定
如何管理数据加载流程 程序员手动编写异步编程代码 自动处理
能不能用代码直接描述验收标准 不能
从RESTful API加载数据并显示所需代码行数 100行 25行

这五篇文章介绍了用 ReactJS 实现复杂交互的前端项目的几个难点,以及 Binding.scala 如何解决这些难点,包括:

\\
  • 复用性\\t
  • 性能和精确性\\t
  • HTML模板\\t
  • 异步编程\

除了上述四个方面以外,ReactJS 的状态管理也是老大难问题,如果引入 Redux 或者 react-router 这样的第三方库来处理状态,会导致架构变复杂,分层变多,代码绕来绕去。而Binding.scala 可以用和页面渲染一样的数据绑定机制描述复杂的状态,不需要任何第三方库,就能提供服务器通信、状态管理和网址分发的功能。

\\

如果你正参与复杂的前端项目,使用ReactJS或其他开发框架时,感到痛苦不堪,你可以用Binding.scala一举解决这些问题。中包含了从零开始创建Binding.scala项目的每一步骤。

\\

五、后记

\\

Everybody’s Got to Learn How to Code

\——奥巴马

\\

编程语言是人和电脑对话的语言。对掌握编程语言的人来说,电脑就是他们大脑的延伸,也是他们身体的一部分。所以,不会编程的人就像是失去翅膀的天使。

\\

电脑程序是很神奇的存在,它可以运行,会看、会听、会说话,就像生命一样。会编程的人就像在创造生命一样,干的是上帝的工作。

\\

我有一个梦想,梦想编程可以像说话、写字一样的基础技能,被每个人都掌握。

\\

如果网页设计师掌握Binding.scala,他们不再需要找工程师实现他们的设计,而只需要在自己的设计稿原型上增加魔法符号.bind,就能创造出会动的网页。

\\

如果QA、BA或产品经理掌握Binding.scala,他们写下验收标准后,不再需要检查程序员干的活对不对,而可以把验收标准自动变成可以运转的功能。

\\

我努力在Binding.scala的设计中消除不必要的技术细节,让人使用Binding.scala时,只需要关注他想传递给电脑的信息。

\\

Binding.scala是我朝着梦想迈进的小小产物。我希望它不光是前端工程师手中的利器,也能成为普通人迈入编程殿堂的踏脚石。

\\

六、相关链接

\\
  • \\t
  • \\t
  • \\t
  • \\t
  • \\t
  • \\t
  • \\t
  • \\t
  • \\t
  • \\t
  • \\t
  • \

七、More than React 系列文章

\\

\\

\\

\\

\\

\\

作者简介

\\

杨博是 Haxe 和 Scala 社区的活跃贡献者,发起和维护的开源项目包括 、、、、、、 。杨博曾在网易任主程序和项目经理,开发过多款游戏。现在ThoughtWorks任Lead Consultant,为客户提供移动、互联网、大数据、人工智能和深度学习领域的解决方案。

\\

感谢对本文的策划,对本文的审校。

转载地址:http://zegxo.baihongyu.com/

你可能感兴趣的文章
windows下的grep
查看>>
【书签】valgrind - the dynamic analysis tools
查看>>
Linux Mysql Related
查看>>
Exception练习-Exception的正确使用
查看>>
switch&router-四层模式
查看>>
新博安卓培训的第一天
查看>>
游戏中常用到的碰撞检测帮助类
查看>>
访问默认共享
查看>>
01262015要看的blog——oracle tuning
查看>>
[信息图]电子商务营销的6大步骤
查看>>
Hibernate注释大全收藏
查看>>
通过openfiler模拟存储
查看>>
java学习笔记 --- String类
查看>>
1.5-cut命令
查看>>
我的友情链接
查看>>
从技术角度看人与人的沟通
查看>>
加速sshd
查看>>
15.3、SElinux介绍
查看>>
关于Nagios Core
查看>>
python基本数据类型的介绍
查看>>