如何将.a文件导入单片机中?

最近用团队的帐号发了一篇文章,初衷是但愿给你们传递一些可能没有接触过的知识,其中某些知识可能也超出了前端的范畴,本质是但愿给你们提供一些扫盲的思路。可是文章的评论使我意识到你们对于这个文章的抵触心情很是大。我有很认真的看你们的每一条评论,而后可能过多的解释也没有什么用。我本身也反思可能文章就不该该以面试为标题进行传播,由于面试的话它就意味着跟职位以及工做息息相关,更况且我仍是以团队的名义去发这个文章。在这里,先跟这些读完文章体验不是很好的同窗道个歉。javascript

之前写文章感受都很开心,写完发完感受都能给你们带来一些新的输入。可是这一次,我感受挺难受的,也确实反思了不少,感受本身以这样的方式传播可能有些问题,主要以下:html

  • 题目取的不对,不该该拿面试做为标题,题目就应该是“脚撕专有钉钉前端面试题”
  • 若是做为面试题,其中某些问题问的太大,范围太广,确实不适合面试者进行回答
  • 若是做为面试题,其中某些问题问的不够专业,甚至是有歧义
  • 给出了面试题,就应该给出面试题的答案,这样才是真正帮助到你们扫盲

这里再也不过多解释和纠结面试题的问题了,由于我感受无论在评论中作什么解释,不承认的同窗仍是会一如既往的怼上来(挺好的,若是怼完感受本身还能释放一些小压力,或许还能适当的给子弈增长一些苍白解释的动力)。固然我也很开心不少同窗在评论中求答案,接下来我会好好认真作一期答案,但愿可以给你们带来一些新的输入,固然答案不可能一会儿作完,也不必定全面或者让你们感受满意,或许你们此次的评论又能给我带来一些学习的机会。前端

舒适提示:这里尽可能多给出一些知识点,因此不会针对问题进行机械式的回答,可能更多的须要你们自行理解和抽象。其中大部分面试题可能会已文章连接的形式出现,或许是我本身之前写过的文章,或者是我以为别人写的不错的文章。java

基础知识主要包含如下几个方面:node

  • 基础:计算机原理、编译原理、数据结构、算法、设计模式、编程范式等基本知识了解
  • 工程:编译工具、格式工具、Git、NPM、单元测试、Nginx、PM二、CI / CD 了解和使用
  • 性能:编译性能、监控、白屏检测、SEO、Service Worker 等了解
  • 后端:Redis 缓存、数据库、Graphql、SSR、模板引擎等了解和使用

现代计算机以存储器为中心,主要由 CPU、I / O 设备以及主存储器三大部分组成。各个部分之间经过总线进行链接通讯,具体以下图所示: 上图是一种多总线结构的示意图,CPU、主存以及 I / O 设备之间的全部数据都是经过总线进行并行传输,使用局部总线是为了提升 CPU 的吞吐量(CPU 不须要直接跟 I / O 设备通讯),而使用高速总线(更贴近 CPU)和 DMA 总线则是为了提高高速 I / O 设备(外设存储器、局域网以及多媒体等)的执行效率。ios

主存包括随机存储器 RAM 和只读存储器 ROM,其中 ROM 又能够分为 MROM(一次性)、PROM、EPROM、EEPROM 。ROM 中存储的程序(例如启动程序、固化程序)和数据(例如常量数据)在断电后不会丢失。RAM 主要分为静态 RAM(SRAM) 和动态 RAM(DRAM) 两种类型(DRAM 种类不少,包括 SDRAM、RDRAM、CDRAM 等),断电后数据会丢失,主要用于存储临时程序或者临时变量数据。 DRAM 通常访问速度相对较慢。因为现代 CPU 读取速度要求相对较高,所以在 CPU 内核中都会设计 L一、L2 以及 L3 级别的多级高速缓存,这些缓存基本是由 SRAM 构成,通常访问速度较快。git

高级程序设计语言不能直接被计算机理解并执行,须要经过翻译程序将其转换成特定处理器上可执行的指令,计算机 CPU 的简单工做原理以下所示: CPU 主要由控制单元、运算单元和存储单元组成(注意忽略了中断系统),各自的做用以下:程序员

  • 控制单元:在节拍脉冲的做用下,将程序计数器(Program Counter,PC)指向的主存或者多级高速缓存中的指令地址送到地址总线,接着获取指令地址所对应的指令并放入指令寄存器 (Instruction Register,IR)中,而后经过指令译码器(Instruction Decoder,ID)分析指令须要进行的操做,最后经过操做控制器(Operation Controller,OC)向其余设备发出微操做控制信号。
  • 运算单元:若是控制单元发出的控制信号存在算术运算(加、减、乘、除、增 一、减 一、取反等)或者逻辑运算(与、或、非、异或),那么须要经过运算单元获取存储单元的计算数据进行处理。
  • 存储单元:包括片内缓存和寄存器组,是 CPU 中临时数据的存储地方。CPU 直接访问主存数据大概须要花费数百个机器周期,而访问寄存器或者片内缓存只须要若干个或者几十个机器周期,所以会使用内部寄存器或缓存来存储和获取临时数据(即将被运算或者运算以后的数据),从而提升 CPU 的运行效率。

除此以外,计算机系统执行程序指令时须要花费时间,其中取出一条指令并执行这条指令的时间叫指令周期。指令周期能够分为若干个阶段(取指周期、间址周期、执行周期和中断周期),每一个阶段主要完成一项基本操做,完成基本操做的时间叫机器周期。机器周期是时钟周期的分频,例如最经典的 8051 单片机的机器周期为 12 个时钟周期。时钟周期是 CPU 工做的基本时间单位,也能够称为节拍脉冲或 T 周期(CPU 主频的倒数) 。假设 CPU 的主频是 1 GHz(1 Hz 表示每秒运行 1 次),那么表示时钟周期为 1 / 109 s。理论上 CPU 的主频越高,程序指令执行的速度越快。es6

上图右侧主存中的指令是 CPU 能够支持的处理命令,通常包含算术指令(加和减)、逻辑指令(与、或和非)、数据指令(移动、输入、删除、加载和存储)、流程控制指令以及程序结束指令等,因为 CPU 只能识别二进制码,所以指令是由二进制码组成。除此以外,指令的集合称为指令集(例如汇编语言就是指令集的一种表现形式),常见的指令集有精简指令集(ARM)和复杂指令集(Inter X86)。通常指令集决定了 CPU 处理器的硬件架构,规定了处理器的相应操做。github

早期的计算机只有机器语言时,程序设计必须用二进制数(0 和 1)来编写程序,而且要求程序员对计算机硬件和指令集很是了解,编程的难度较大,操做极易出错。为了解决机器语言的编程问题,慢慢开始出现了符号式的汇编语言(采用 ADD、SUB、MUL、DIV 等符号表明加减乘除)。为了使得计算机能够识别汇编语言,须要将汇编语言翻译成机器可以识别的机器语言(处理器的指令集): 因为每一种机器的指令系统不一样,须要不一样的汇编语言程序与之匹配,所以程序员每每须要针对不一样的机器了解其硬件结构和指令系统。为了能够抹平不一样机器的指令系统,使得程序员能够更加关注程序设计自己,前后出现了各类面向问题的高级程序设计语言,例如 BASIC 和 C,具体过程以下图所示: 高级程序语言会先翻译成汇编语言或者其余中间语言,而后再根据不一样的机器翻译成机器语言进行执行。除此以外,汇编语言虚拟机和机器语言机器之间还存在一层操做系统虚拟机,主要用于控制和管理操做系统的所有硬件和软件资源(随着超大规模集成电路技术的不断发展,一些操做系统的软件功能逐步由硬件来替换,例如目前的操做系统已经实现了部分程序的固化,简称固件,将程序永久性的存储在 ROM 中)。机器语言机器还能够继续分解成微程序机器,将每一条机器指令翻译成一组微指令(微程序)进行执行。

上述虚拟机所提供的语言转换程序被称为编译器,主要做用是将某种语言编写的源程序转换成一个等价的机器语言程序,编译器的做用以下图所示: 例如 C 语言,能够先经过 gcc 编译器生成 Linux 和 Windows 下的目标 .o 和 .obj 文件(object 文件,即目标文件),而后将目标文件与底层系统库文件、应用程序库文件以及启动文件连接成可执行文件在目标机器上执行。

舒适提示:感兴趣的同窗能够了解一下 ARM 芯片的程序运行原理,包括使用 IDE 进行程序的编译(IDE 内置编译器,主流编译器包含 ARMCC、IAR 以及 GCC FOR ARM 等,其中一些编译器仅仅随着 IDE 进行捆绑发布,不提供独立使用的能力,而一些编译器则随着 IDE 进行发布的同时,还提供命令行接口的独立使用方式)、经过串口进行程序下载(下载到芯片的代码区初始启动地址映射的存储空间地址)、启动的存储空间地址映射(包括系统存储器、闪存 FLASH、内置 SRAM 等)、芯片的程序启动模式引脚 BOOT 的设置(例如调试代码时经常选择内置 SRAM、真正程序运行的时候选择闪存 FLASH)等。

若是某种高级语言或者应用语言(例如用于人工智能的计算机设计语言)转换的目标语言不是特定计算机的汇编语言,而是面向另外一种高级程序语言(不少研究性的编译器将 C 做为目标语言),那么还须要将目标高级程序语言再进行一次额外的编译才能获得最终的目标程序,这种编译器可称为源到源的转换器。

除此以外,有些程序设计语言将编译的过程和最终转换成目标程序进行执行的过程混合在一块儿,这种语言转换程序一般被称为解释器,主要做用是将某种语言编写的源程序做为输入,将该源程序执行的结果做为输出,解释器的做用以下图所示:

解释器和编译器有不少类似之处,都须要对源程序进行分析,并转换成目标机器可识别的机器语言进行执行。只是解释器是在转换源程序的同时立马执行对应的机器语言(转换和执行的过程不分离),而编译器得先把源程序所有转换成机器语言并产生目标文件,而后将目标文件写入相应的程序存储器进行执行(转换和执行的过程分离)。例如 Perl、Scheme、APL 使用解释器进行转换, C、C++ 则使用编译器进行转换,而 Java 和 JavaScript 的转换既包含了编译过程,也包含了解释过程。

JavaScript 中的数组存储大体须要分为两种状况:

  • 同种类型数据的数组分配连续的内存空间
  • 存在非同种类型数据的数组使用哈希映射分配内存空间

舒适提示:能够想象一下连续的内存空间只须要根据索引(指针)直接计算存储位置便可。若是是哈希映射那么首先须要计算索引值,而后若是索引值有冲突的场景下还须要进行二次查找(须要知道哈希的存储方式)。

编译器的设计是一个很是庞大和复杂的软件系统设计,在真正设计的时候须要解决两个相对重要的问题:

  • 如何分析不一样高级程序语言设计的源程序
  • 如何将源程序的功能等价映射到不一样指令系统的目标机器

为了解决上述两项问题,编译器的设计最终被分解成前端(注意这里所说的不是 Web 前端)和后端两个编译阶段,前端用于解决第一个问题,然后端用于解决第二个问题,具体以下图所示: 上图中的中间表示(Intermediate Representation,IR)是程序结构的一种表现方式,它会比 AST(后续讲解)更加接近汇编语言或者指令集,同时也会保留源程序中的一些高级信息,除此以外 ,它的种类不少,包括、以及基于栈的 IR 等,具体做用包括:

  • 靠近前端部分主要适配不一样的源程序,靠近后端部分主要适配不一样的指令集,更易于编译器的错误调试,容易识别是 IR 以前仍是以后出问题
  • 以下左图所示,若是没有 IR,那么源程序到指令集之间须要进行一一适配,而有了中间表示,则可使得编译器的职责更加分离,源程序的编译更多关注如何转换成 IR,而不是去适配不一样的指令集
  • IR 自己能够作到多趟迭代从而优化源程序,在每一趟迭代的过程当中能够研究代码并记录优化的细节,方便后续的迭代查找并利用这些优化信息,最终能够高效输出更优的目标程序

因为 IR 能够进行多趟迭代进行程序优化,所以在编译器中可插入一个新的优化阶段,以下图所示: 优化器能够对 IR 处理一遍或者多遍,从而生成更快执行速度(例如找到循环中不变的计算并对其进行优化从而减小运算次数)或者更小体积的目标程序,也可能用于产生更少异常或者更低功耗的目标程序。除此以外,前端和后端内部还能够细分为多个处理步骤,具体以下图所示: 优化器中的每一遍优化处理均可以使用一个或多个优化技术来改进代码,每一趟处理最终都是读写 IR 的操做,这样不只仅可使得优化能够更加高效,同时也能够下降优化的复杂度,还提升了优化的灵活性,可使得编译器配置不一样的优化选项,达到组合优化的效果。

观察者模式和发布/订阅模式

编程范式(Programming paradigm)是指计算机编程的基本风格或者典型模式,能够简单理解为编程学科中实践出来的具备哲学和理论依据的一些经典原型。常见的编程范式有:

阅读连接::若是你对于编程范式的定义相对模糊,能够继续阅读 了解更多。

不一样的语言能够支持多种不一样的编程范式,例如 C 语言支持 POP 范式,C++ 和 Java 语言支持 OOP 范式,Swift 语言则能够支持 FP 范式,而 Web 前端中的 JavaScript 能够支持上述列出的全部编程范式。

顾名思义,函数式编程是使用函数来进行高效处理数据或数据流的一种编程方式。在数学中,函数的三要素是定义域、值域和**对应关系。假设 A、B 是非空数集,对于集合 A 中的任意一个数 x,在集合 B 中都有惟一肯定的数 f(x) 和它对应,那么能够将 f 称为从 A 到 B 的一个函数,记做:y = f(x)。在函数式编程中函数的概念和数学函数的概念相似,主要是描述形参 x 和返回值 y 之间的对应关系,**以下图所示:

舒适提示:图片来自于。

在实际的编程中,能够将各类明确对应关系的函数进行传递、组合从而达处处理数据的最终目的。在此过程当中,咱们的关注点不在于如何去实现**对应关系,**而在于如何将各类已有的对应关系进行高效联动,从而可快速进行数据转换,达到最终的数据处理目的,提供开发效率。

尽管你对函数式编程的概念有所了解,可是你仍然不知道函数式编程到底有什么特色。这里咱们仍然拿 OOP 编程范式来举例,假设但愿经过 OOP 编程来解决数学的加减乘除问题:

以上程序计算的结果是 8,彻底符合预期。咱们知道了 addmultiply 两个函数的实际对应关系,经过将对应关系进行有效的组合和传递,达到了最终的计算结果。除此以外,这两个函数还能够根据数学定律得出更优雅的组合方式:

咱们彻底不须要追踪相似于 OOP 编程范式中可能存在的内部状态数据,事实上对于数学定律中的结合律、交换律、同一概以及分配律,上述的函数式编程代码足能够胜任。

经过上述简单的例子能够发现,要实现高可复用的函数**(对应关系)**,必定要遵循某些特定的原则,不然在使用的时候可能没法进行高效的传递和组合,例如

若是你以前常常进行无原则性的代码设计,那么在设计过程当中可能会出现各类出乎意料的问题(这是为何新手总是出现一些稀奇古怪问题的主要缘由)。函数式编程能够有效的经过一些原则性的约束使你设计出更加健壮和优雅的代码,而且在不断的实践过程当中进行经验式叠加,从而提升开发效率。

虽然咱们在使用函数的过程当中更多的再也不关注函数如何实现(对应关系),可是真正在使用和设计函数的时候须要注意如下一些特色:

咱们之前设计的代码一般是命令式编程方式,这种编程方式每每注重具体的实现的过程(对应关系),而函数式编程则采用声明式的编程方式,每每注重如何去组合已有的**对应关系。**简单举个例子:

 
命令式代码一步一步的告诉计算机须要执行哪些语句,须要关心变量的实例化状况、循环的具体过程以及跟踪变量状态的变化过程。声明式代码更多的再也不关心代码的具体执行过程,而是采用表达式的组合变换去处理问题,再也不强调怎么作,而是指明**作什么。**声明式编程方式能够将咱们设计代码的关注点完全从过程式解放出来,从而提升开发效率。

在 JavaScript 中,函数的使用很是灵活,例如能够对函数进行如下操做:
经过以上示例能够看出,函数继承至对象并拥有对象的特性。在 JavaScript 中能够对函数进行参数传递、变量赋值或数组操做等等,所以把函数称为一等公民。函数式编程的核心就是对函数进行组合或传递,JavaScript 中函数这种灵活的特性是知足函数式编程的重要条件。

纯函数是是指在相同的参数调用下,函数的返回值惟一不变。这跟数学中函数的映射关系相似,一样的 x 不可能映射多个不一样的 y。使用函数式编程会使得函数的调用很是稳定,从而下降 Bug 产生的机率。固然要实现纯函数的这种特性,须要函数不能包含如下一些反作用:
  • 可变数据(包括在函数内部改变输入参数)
 
从以上常见的一些反作用能够看出,纯函数的实现须要遵循最小意外原则,为了确保函数的稳定惟一的输入和输出,尽可能应该避免与函数外部的环境进行任何交互行为,从而防止外部环境对函数内部产生没法预料的影响。纯函数的实现应该自给自足,举几个例子:
 
 
纯函数的特性使得函数式编程具有如下特性:
 
可缓存性和可测试性基于纯函数输入输出惟一不变的特性,可移植性则主要基于纯函数不依赖外部环境的特性。这里举一个可缓存的例子:

在函数式编程的简单示例中已经能够清晰的感觉到函数式编程绝对不能依赖内部状态,而在纯函数中则说明了函数式编程不能依赖外部的环境或状态,由于一旦依赖的状态变化,不能保证函数根据对应关系所计算的返回值由于状态的变化仍然保持不变。
这里单独讲解一下数据不可变,在 JavaScript 中有不少数组操做的方法,举个例子:
这里的 slice 方法屡次调用都不会改变原有数组,且会产生相同的输出。而 splice 每次调用都在修改原数组,且产生的输出也不相同。 在函数式编程中,这种会改变原有数据的函数已经再也不是纯函数,应该尽可能避免使用。

阅读连接:若是想要了解更深刻的函数式编程知识点,能够额外阅读。

 
 
响应式编程是一种基于观察者(发布 / 订阅)模式而且面向异步(Asynchronous)数据流(Data Stream)和变化传播的声明式编程范式。响应式编程主要适用的场景包含:
  • 用户和系统发起的连续事件处理,例如鼠标的点击、键盘的按键或者通讯设备发起的信号等
  • 非可靠的网络或者通讯处理(例如 HTTP 网络的请求重试)
  • 连续的异步 IO 处理
  • 复杂的继发事务处理(例如一次事件涉及到多个继发的网络请求)
  • 高并发的消息处理(例如 IM 聊天)
 
 
 
 
 



这里列出一些我经常使用的 API:
 

在 JavaScript 中利用(Event Loop)能够在单线程中实现非阻塞式、异步的操做。例如
 
咱们重点来看一下经常使用的几种编程方式(Callback、Promise、Generator、Async)在语法糖上带来的优劣对比。

Callback(回调函数)是在 Web 前端开发中常常会使用的编程方式。这里举一个经常使用的定时器示例:
回调函数常常会由于调用环境的变化而致使 this 的指向性变化。除此以外,使用回调函数来处理多个继发的异步任务时容易致使回调地狱(Callback Hell):
回调函数不能经过 return 返回数据,好比咱们但愿调用带有回调参数的函数并返回异步执行的结果时,只能经过再次回调的方式进行参数传递:
 

上述示例讲述的都是 JavaScript 中标准的异步 API ,若是使用一些三方的异步 API 而且提供了回调能力时,这些 API 多是非受信的,在真正使用的时候会由于执行反转(回调函数的执行权在三方库中)致使如下一些问题:
  • 使用者的回调函数设计没有进行错误捕获,而偏偏三方库进行了错误捕获却没有抛出错误处理信息,此时使用者很难感知到本身设计的回调函数是否有错误
  • 使用者难以感知到三方库的回调时机和回调次数,这个回调函数执行的权利控制在三方库手中
  • 使用者没法更改三方库提供的回调参数,回调参数可能没法知足使用者的诉求
 


Callback 的异步操做形式除了会形成回调地狱,还会形成难以测试的问题。ES6 中的 Promise (基于 规范的异步编程解决方案)利用的原理来解决异步的处理问题,Promise 对象提供了统一的异步编程 API,它的特色以下:
  • Promise 对象的执行状态不受外界影响。Promise 对象的异步操做有三种状态: pending(进行中)、 fulfilled(已成功)和 rejected(已失败) ,只有 Promise 对象自己的异步操做结果能够决定当前的执行状态,任何其余的操做没法改变状态的结果
 

舒适提示:有限状态机提供了一种优雅的解决方式,异步的处理自己能够经过异步状态的变化来触发相应的操做,这会比回调函数在逻辑上的处理更加合理,也能够下降代码的复杂度。

 
Promise 对象的执行状态不可变示例以下:
假设要实现两个继发的 HTTP 请求,第一个请求接口返回的数据是第二个请求接口的参数,使用回调函数的实现方式以下所示(这里使用 setTimeout 来指代异步请求):
 
 
 

舒适提示:继发请求的依赖关系很是常见,例如人员基本信息管理系统的开发中,常常须要先展现组织树结构,并默认加载第一个组织下的人员列表信息。

 
若是采用 Promise 的处理方式则能够规避上述常见的回调地狱问题:

首先 Promise 能够注册多个 then(放在一个执行队列里),而且这些 then 会根据上一次返回值的结果依次执行。除此以外,各个 Promise 的 then 执行互不干扰。 咱们将示例进行简单的变换:
上述例子中的执行结果是由于 then 的执行会返回一个新的 Promise 对象,而且若是 then 执行后返回的仍然是 Promise 对象,那么下一个 then 的链式调用会等待该 Promise 对象的状态发生变化后才会调用(能获得这个 Promise 处理的结果)。接下来重点看下 Promise
从上述示例能够看出 Promise 的错误不会影响其余代码的执行,只会影响 Promise 内部的代码自己,由于Promise 会在内部对错误进行异常捕获,从而保证总体代码执行的稳定性。Promise 还提供了其余的一些 API 方便多任务的执行,包括
  • Promise.all:适合多个异步任务并发执行但不容许其中任何一个任务失败
  • Promise.race :适合多个异步任务抢占式执行
  • Promise.allSettled :适合多个异步任务并发执行但容许某些任务失败
 
Promise 相对于 Callback 对于异步的处理更加优雅,而且能力也更增强大, 可是也存在一些自身的缺点:
  • 状态单一,每次决断只能产生一种状态结果,须要不停的进行链式调用
 

舒适提示:手写 Promise 是面试官很是喜欢的一道笔试题,本质是但愿面试者可以经过底层的设计正确了解 Promise 的使用方式,若是你对 Promise 的设计原理不熟悉,能够深刻了解一下或者手动设计一个。

 

Promise 解决了 Callback 的回调地狱问题,但也形成了代码冗余,若是一些异步任务不支持 Promise 语法,就须要进行一层 Promise 封装。Generator 将 JavaScript 的异步编程带入了一个全新的阶段,它使得异步代码的设计和执行看起来和同步代码一致。Generator 使用的简单示例以下:
经过上述代码,能够看出 Generator 相对于 Promise 具备如下优点:
  • 丰富了状态类型,Generator 经过 next 能够产生不一样的状态信息,也能够经过 return 结束函数的执行状态,相对于 Promise 的 resolve 不可变状态更加丰富 
  • Generator 函数内部的异步代码执行看起来和同步代码执行一致,很是利于代码的维护
  • Generator 函数内部的执行逻辑和相应的状态变化逻辑解耦,下降了代码的复杂度
 
next 能够不停的改变状态使得 yield 得以继续执行的代码能够变得很是有规律,例如从上述的手动执行 Generator 函数能够看出,彻底能够将其封装成一个自动执行的执行器,具体以下所示:

舒适提示:设计了一个 Generator 自动执行器 ,使用 Co 的前提是 yield  命令后必须是 Promise 对象或者 Thunk 函数。Co 还能够支持并发的异步处理,具体可查看官方的 。

 
须要注意的是 Generator 函数的返回值是一个 Iterator 遍历器对象,具体以下所示:
Generator 函数的错误处理相对复杂一些,极端状况下须要对执行和 Generator 函数进行双重错误捕获,具体以下所示:
在使用 g.throw 的时候还须要注意如下一些事项:
  • 若是 Generator 函数自己没有捕获错误,那么 Generator 函数内部抛出的错误能够在执行处进行错误捕获
  • 若是 Generator 函数内部和执行处都没有进行错误捕获,则终止进程并抛出错误信息
  • 若是没有执行过 g.next,则 g.throw 不会在 Gererator 函数中被捕获(由于执行指针没有启动 Generator 函数的执行),此时能够在执行处进行执行错误捕获
 

  • 内置执行器:Generator 函数须要设计手动执行器或者通用执行器(例如 Co 执行器)进行执行,Async 语法则内置了自动执行器,设计代码时无须关心执行步骤
 

经过上述示例能够看出,async 函数的特性以下:
  • 调用 async 函数后返回的 Promise 对象必须等待内部全部 await 对应的 Promise 执行完(这使得 async 函数多是阻塞式执行)后才会发生状态变化,除非中途遇到了 return 语句
  • await 命令后若是是 Promise 对象,则返回 Promise 对象处理后的结果,若是是原始数据类型,则直接返回原始数据类型
 
上述代码是阻塞式执行,nextPromise 须要等待 firstPromise 执行完成后才能继续执行,若是但愿二者可以并发执行,则能够进行下述设计:
除了使用 Promise 自带的并发执行 API,也能够经过让全部的 Promise 提早并发执行来处理:
Async 的错误处理相对于 Generator 会更加简单,具体示例以下所示:
async 函数内部抛出的错误,会致使函数返回的 Promise 对象变为 rejected 状态,从而能够经过 catch 捕获, 上述代码只是一个粗粒度的容错处理,若是但愿 firstPromise 错误后能够继续执行
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
业务思考更多的是结合基础知识的广度和深度进行的具体业务实践,主要包含如下几个方面:
  • 通用性:脚手架、SDK、组件库等框架设计
  • 低代码:通用表单设计、通用布局设计、通用页面设计、JSON Schema 协议设计等
  • 测试:E2E 测试、单元测试、测试覆盖率、测试报告等
  • 业务:数据、体验、复杂度、监控
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
笔试更多的是考验应聘者的逻辑思惟能力和代码书写风格,主要包含如下几个方面:
 
 
 
 
 
 
 
 
 
}

我要回帖

更多关于 怎么把keil5程序导入单片机 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信