文档章节

elixir官方入门教程 尝试,抓取和解救

ljzn
 ljzn
发布于 2016/08/06 12:03
字数 1783
阅读 28
收藏 0
点赞 0
评论 0

#尝试,抓取和解救

  1. 错误
  2. 抛出
  3. 退出
  4. 之后
  5. 变量域

Elixir有三种异常机制:错误,引发和退出.本章我们将探索它们每一种以及其使用的场合.

#错误

错误(或 异常 )用于代码中发生异常时.当试图将一个数字与原子相加,就可得到一个错误的例子:

iex> :foo + 1
** (ArithmeticError) bad argument in arithmetic expression
     :erlang.+(:foo, 1)

一个运行时错误可有raise/1引发:

iex> raise "oops"
** (RuntimeError) oops

其它错误可以由raise/2引发,通过传送错误名称和一个关键词列表作为参数:

iex> raise ArgumentError, message: "invalid argument foo"
** (ArgumentError) invalid argument foo

你也可以通过在一个模块中使用defexception结构来定义你自己的错误;这时你创造了一个与模块同名的错误.最常用的场景是定义一个带信息场的异常:

iex> defmodule MyError do
iex>   defexception message: "default message"
iex> end
iex> raise MyError
** (MyError) default message
iex> raise MyError, message: "custom message"
** (MyError) custom message

错误可以被解救,通过try/rescue结构:

iex> try do
...>   raise "oops"
...> rescue
...>   e in RuntimeError -> e
...> end
%RuntimeError{message: "oops"}

上述例子将运行时错误解救,并返回错误本身,然后将其打印到iex中.

如果错误对你毫无用处,你可以不显示它:

iex> try do
...>   raise "oops"
...> rescue
...>   RuntimeError -> "Error!"
...> end
"Error!"

实际中,Elixir开发者很少用到try/rescue结构.例如,当文件无法被打开时,许多语言会强制你解救这个错误.作为替代,Elixir中提供了File.read/1函数,其会返回一个包含文件是否被成功打开的信息的元组.

iex> File.read "hello"
{:error, :enoent}
iex> File.write "hello", "world"
:ok
iex> File.read "hello"
{:ok, "world"}

这里没有try/rescue.如果你想要处理打开文件时的不同输出,你可以简单地使用case来进行模式匹配:

iex> case File.read "hello" do
...>   {:ok, body}      -> IO.puts "Success: #{body}"
...>   {:error, reason} -> IO.puts "Error: #{reason}"
...> end

最终,打开文件时发生的错误是否为异常将由你的应用来决定.这就是Elixir为何不给File.read/1和其它许多函数强加异常.而是留给开发者来选择最好的处理方式.

当你确信一个文件存在(缺失文件确实是错误的),你可以简单地使用File.read!/1:

iex> File.read! "unknown"
** (File.Error) could not read file unknown: no such file or directory
    (elixir) lib/file.ex:305: File.read!/1

标准库中的许多函数遵循对应的异常引发模式,而非返回匹配元组.函数foo会返回{:ok, result}{:error, reason}元组,而另一个函数(foo!,同名但带有!)虽然接受与foo同样的参数,但遇到错误时会抛出异常.如果一切正常,foo!会返回(没有被元组包裹的)结果.File模块就是很好的例子.

在Elixir中,我们避免使用try/rescue,因为我们不在控制流中使用错误.我们这样解释错误:它们是预留给意料外或异常的情形的.当你需要使用控制流结构时,应该使用_抛出_.下面我们将讲到.

#抛出

在Elixir中,一个值可以被抛出然后被捕获.throwcatch是预留给那些只有它们才能检索到的值的.

这些情况很少遇到,除了当与没有提供合适的API的库相连接时.例如,想象一下Enum模块没有提供任何API来找到一个值,而我们需要从一个数字列表中找到第一个13的倍数:

iex> try do
...>   Enum.each -50..50, fn(x) ->
...>     if rem(x, 13) == 0, do: throw(x)
...>   end
...>   "Got nothing"
...> catch
...>   x -> "Got #{x}"
...> end
"Got -39"

由于Enum实际上 提供了合适的API,所以可以使用Enum.find/2:

iex> Enum.find -50..50, &(rem(&1, 13) == 0)
-39

#退出

进程中运行的所有Elixir代码都会互相交流.当一个进程因"自然原因"(未处理的异常)死亡时,就会发送一个exit信号.直接发送一个退出信号也会让进程死亡:

iex> spawn_link fn -> exit(1) end
#PID<0.56.0>
** (EXIT from #PID<0.56.0>) 1

上述例子中,链接的进程死了,是因为发送了一个值为1的exit信号.Elixir壳自动处理了这些信息并将其打印到终端.

exit也可以被try/catch"捕获":

iex> try do
...>   exit "I am exiting"
...> catch
...>   :exit, _ -> "not really"
...> end
"not really"

使用try/catch已经很不常见,用它来捕获退出就更少见了.

exit信号是由Erlang VM提供的容错系统中重要的部分.进程通常在监督树下运行,这些树是自己处理的,只是等待来自监督过程中的退出信号.一旦接收到了退出信号,监督策略将启动,并重启监督进程.

正是由于监督系统的存在,使得try/catchtry/rescue结构在Elixir中如此少见.语气解救一个错误,我们更愿意"快速失败",因为监督树会保证我们的应用能够在出错之后回到一个已知的初始状态.

#之后

在资源经过某些有可能引发错误的操作之后,我们需要确认其已经被清理干净了.try/after结构允许你这样做.例如,我们可以打开一个文件然后使用一个after从句来关闭它--即使粗错了:

iex> {:ok, file} = File.open "sample", [:utf8, :write]
iex> try do
...>   IO.write file, "olá"
...>   raise "oops, something went wrong"
...> after
...>   File.close(file)
...> end
** (RuntimeError) oops, something went wrong

无论try块中的代码是否成功,after从句都会被执行.然而,注意,如果一个链接进程退出了,那么这个进程会立刻退出而且不会执行after从句.因此after只提供了一个软保险.幸运的是,Elixir中的文件也链接到了当前进程,所以当前进程崩溃时它们总会被关闭,这是独立于after从句的.你会发现对于其他资源例如ETS表,套接字,端口等等也是成立的.

有时你会想要将整个函数包裹在try结构内,通常是为了保证这些代码在之后能被执行.这时,Elixir允许你省略try这一行:

iex> defmodule RunAfter do
...>   def without_even_trying do
...>     raise "oops"
...>   after
...>     IO.puts "cleaning up!"
...>   end
...> end
iex> RunAfter.without_even_trying
cleaning up!
** (RuntimeError) oops

Elixir会自动将函数体包裹到try里,无论选择after,rescuecatch中的哪一个.

#变量域

要牢记try/catch/rescue/after块中的变量定义不会泄露到外部内容中.这是因为try块可能会失败,因此这些变量可能永远不会被绑定在第一位.换句话说,这个代码是非法的:

iex> try do
...>   raise "fail"
...>   what_happened = :did_not_raise
...> rescue
...>   _ -> what_happened = :rescued
...> end
iex> what_happened
** (RuntimeError) undefined function: what_happened/0

作为替代,你可以存储try表达式的值:

iex> what_happened =
...>   try do
...>     raise "fail"
...>     :did_not_raise
...>   rescue
...>     _ -> :rescued
...>   end
iex> what_happened
:rescued

try,catchrescue的介绍到此结束.你会发现相较于其他语言,在Elixir中较少用到它们,尽管在某些库或特定的代码没有"按照规则"书写时,它们很有用.

© 著作权归作者所有

共有 人打赏支持
ljzn
粉丝 29
博文 69
码字总数 96245
作品 0
南平
程序员
Elixir 学习资源

Elixir 官网 getting started官方入门学习资源 官方文档 hex 包管理系统 elixir sips 比较不错视频课程 Elixir China 中文论坛 官方wiki Elixir by Example Awesome Elixir Elixir Quiz 通过...

lidashuang
2017/11/29
0
0
elixir官方入门教程 介绍

介绍 安装 交互模式 运行脚本 提出疑问 欢迎! 在本教程中我们将教给你Elixir的基础,语法,如何定义模块,如何操作常用数据结构的特性等等.本章将确保Elixir安装好了,并且你能够成功运行Elixir的...

ljzn
2016/08/06
70
0
elixir官方入门教程 学习资料

下一步该去哪 构建你的第一个Elixir项目 元编程 社区与其它资源 Erlang基础 想要学习更多?继续阅读! 构建你的第一个Elixir项目 为了开始你的第一个项目,Elixir装载了一个叫做Mix的构建工具....

ljzn
2016/08/06
144
0
elixir官方入门教程 模块

模块 编译 脚本模式 具名函数 函数捕获 默认参数 在Elixir中我们将一些函数集合到模块里。在之前的章节里我们已经使用了许多不同的模块,例如模块: 为了创造我们自己的模块,需要用到宏。我...

ljzn
2016/08/03
54
0
总有你要的编程书单(GitHub )

目录 IDE IntelliJ IDEA 简体中文专题教程 MySQL 21分钟MySQL入门教程 MySQL索引背后的数据结构及算法原理 NoSQL Disque 使用教程 Neo4j .rb 中文資源 Redis 命令参考 Redis 设计与实现 The ...

汇智网
2017/11/22
0
0
elixir官方教程Mix与OTP(一) Mix入门

Mix入门 我们的第一个项目 编辑项目 执行测试 环境 探索 在本教程中,我们将学习如何构建一个完整的Elixir应用,包括监督树,配置,测试等等. 这个应用的功能是分布式键值仓库.我们将把键值对安排...

ljzn
2016/08/07
1K
2
elixir官方入门教程 进程

进程 和 链接 任务 状态 在Elixir中,所有代码都运行在进程内。进程相互独立,并发地运行,通过传送信息来交流。进程不是Elixir中唯一的并发基础,但它意味着能够构建分布式的,可容错的程序...

ljzn
2016/08/04
34
0
elixir官方入门教程 别名,要求与进口

别名,要求与进口 别名 要求 进口 使用 理解别名 模块嵌套 群体别名/进口/要求/使用 为了方便软件复用,Elixir提供了三个命令(,和)外加一个宏,简介如下: 现在我们将详细探索它们.记住前三条之所...

ljzn
2016/08/04
255
2
开源电子书

目录 语言无关类 操作系统 智能系统 分布式系统 编译原理 函数式概念 计算机图形学 WEB服务器 版本控制 编辑器 NoSQL PostgreSQL MySQL 管理和监控 项目相关 设计模式 Web 大数据 编程艺术 ...

zting科技
2017/12/11
0
0
elixir官方入门教程 Erlang库

Erlang库 二进制模块 格式化文本输出 加密模块 图片模块 Erlang长期存储 数学模块 队列模块 随机模块 压缩模块 Elixir提供了与Erlang库优秀的互用性.事实上,Elixir不鼓励简单地包装Erlang库,...

ljzn
2016/08/06
59
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

50 行 Python 代码,带你追到最心爱的人

程序员世纪难题 人们一提到程序员第一反应就是:我知道!他们工资很高啊!但大部分都是单身狗,不懂得幽默风趣,只是每天穿格子 polo 衫的宅男一个。甚至程序员自己也这样形容自己:钱多话少...

猫咪编程
3分钟前
0
0
JAVA知识点随心记

1.Switch case具体的支持类型? Q:支持byte、short、char、int基本类型,枚举类型和String类型(JDK7以上支持),四种基本类型的包装类型也支持,但是原因在于触发了自动拆箱,将包装类型拆成了基本...

勤奋的蚂蚁
14分钟前
0
0
NoSQL

一、NoSQL介绍 NoSQL属于非关系型数据,mysql属于关系型数据库。 对于关系型数据库来说,是需要把数据存储到库、表、行、字段里,查询的时候根据条件一行一行地去匹配,当数据量非常大的时候...

人在艹木中
19分钟前
0
0
第17章MySQL主从配置

mysql安装总结 mysql主从准备工作: 准备两台机器,每台机器安装msyql服务,并启动mysql服务 mysql详细安装 1.首先下载二进制免编译的包,下载到/usr/local/src/目录下 2.解压压缩包 3.解压完...

Linux学习笔记
22分钟前
0
0
Redis高可用及分片集群

一、主从复制 使用异步复制 一个服务器可以有多个从服务器 从服务器也可以有自己的从服务器 复制功能不会阻塞主服务器 可以通过服务功能来上主服务器免于持久化操作,由从服务器去执行持久化...

Java大蜗牛
26分钟前
0
0
前端面试题汇总

最近在复习,准备找工作了,特此总结一下前端的相关知识。 1.获取浏览器URL中查询字符的参数: function getQuery(name){    var reg = new RegExp("(^|&)"+name+"=([^&]*)"(&|$));...

凛冬来袭
今天
0
0
可持续发展的学习道路

与其要求别人,不如提升自己 内心渴望进步 经常做出改变现有模式,不断学习 寻找资源,整合资源,不断熟练这种模式 渠道很重要 先打开新世界的航路

狮子狗
今天
0
0
apollox-lua开源项目 示例codepen2

今天在示例上增加了几个功能, 首先添加js array的标准库。 所有js array的方法目前都支持了。 添加查看code模式。 点击查看code可以看到生成的lua代码。默认web模式需要把标准库连接进来, ...

钟元OSS
今天
0
0
javascript性能优化之避免重复工作

javascript最重要也最根本的性能优化标准之一是避免工作,避免工作又包括两点,第一,不做不必要的工作,第二,不做重复的已经完成的工作。第一部分可以通过代码重构完成,第二部分不做重复的...

老韭菜
今天
0
0
缓存穿透、并发和雪崩那些事

0 题记 缓存穿透、缓存并发和缓存雪崩是常见的由于并发量大而导致的缓存问题,本文讲解其产生原因和解决方案。 缓存穿透通常是由恶意攻击或者无意造成的;缓存并发是由设计不足造成的;缓存雪...

Java填坑之路
今天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部