170+道钉钉前端扫盲知识点哈喽比特

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

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

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

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

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

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

JavaScript中的数组存储大致需要分为两种情况:

温馨提示:可以想象一下连续的内存空间只需要根据索引(指针)直接计算存储位置即可。如果是哈希映射那么首先需要计算索引值,然后如果索引值有冲突的场景下还需要进行二次查找(需要知道哈希的存储方式)。

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

阅读链接:基于Vue实现一个简易MVVM[5]-观察者模式和发布/订阅模式

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

阅读链接::如果你对于编程范式的定义相对模糊,可以继续阅读Whatistheprecisedefinitionofprogrammingparadigm\[6]了解更多。

不同的语言可以支持多种不同的编程范式,例如C语言支持POP范式,C++和Java语言支持OOP范式,Swift语言则可以支持FP范式,而Web前端中的JavaScript可以支持上述列出的所有编程范式。

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

温馨提示:图片来自于简明JavaScript函数式编程——入门篇[7]。

简单示例

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

classMathObject{constructor(privatevalue:number){}publicadd(num:number):MathObject{this.value+=num;returnthis;}publicmultiply(num:number):MathObject{this.value*=num;returnthis;}publicgetValue():number{returnthis.value;}}consta=newMathObject(1);a.add(1).multiply(2).add(a.multiply(2).getValue());复制代码我们希望通过上述程序来解决(1+2)*2+1*2的问题,但实际上计算出来的结果是24,因为在代码内部有一个this.value的状态值需要跟踪,这会使得结果不符合预期。接下来我们采用函数式编程的方式:

functionadd(a:number,b:number):number{returna+b;}functionmultiply(a:number,b:number):number{returna*b;}consta:number=1;constb:number=2;add(multiply(add(a,b),b),multiply(a,b));复制代码以上程序计算的结果是8,完全符合预期。我们知道了add和multiply两个函数的实际对应关系,通过将对应关系进行有效的组合和传递,达到了最终的计算结果。除此之外,这两个函数还可以根据数学定律得出更优雅的组合方式:

add(multiply(add(a,b),b),multiply(a,b));//根据数学定律分配律:a*b+a*c=a*(b+c),得出://(a+b)*b+a*b=(2a+b)*b//简化上述函数的组合方式multiply(add(add(a,a),b),b);复制代码我们完全不需要追踪类似于OOP编程范式中可能存在的内部状态数据,事实上对于数学定律中的结合律、交换律、同一律以及分配律,上述的函数式编程代码足可以胜任。

原则

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

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

特点

一等公民

在JavaScript中,函数的使用非常灵活,例如可以对函数进行以下操作:

纯函数

纯函数是是指在相同的参数调用下,函数的返回值唯一不变。这跟数学中函数的映射关系类似,同样的x不可能映射多个不同的y。使用函数式编程会使得函数的调用非常稳定,从而降低Bug产生的机率。当然要实现纯函数的这种特性,需要函数不能包含以下一些副作用:

从以上常见的一些副作用可以看出,纯函数的实现需要遵循最小意外原则,为了确保函数的稳定唯一的输入和输出,尽量应该避免与函数外部的环境进行任何交互行为,从而防止外部环境对函数内部产生无法预料的影响。纯函数的实现应该自给自足,举几个例子:

可缓存性和可测试性基于纯函数输入输出唯一不变的特性,可移植性则主要基于纯函数不依赖外部环境的特性。这里举一个可缓存的例子:

interfaceICache{[arg:string]:T;}interfaceISquare{(x:T):T;}//简单的缓存函数(忽略通用性和健壮性)functionmemoize(fn:ISquare):ISquare{constcache:ICache={};returnfunction(x:T){constarg:string=JSON.stringify(x);cache[arg]=cache[arg]||fn.call(fn,x);returncache[arg];};}//纯函数functionsquare(x:number):number{returnx*x;}constmemoSquare=memoize(square);memoSquare(4);//不会再次调用纯函数square,而是直接从缓存中获取值//由于输入和输出的唯一性,获取缓存结果可靠稳定//提升代码的运行效率memoSquare(4);复制代码无状态和数据不可变

在函数式编程的简单示例中已经可以清晰的感受到函数式编程绝对不能依赖内部状态,而在纯函数中则说明了函数式编程不能依赖外部的环境或状态,因为一旦依赖的状态变化,不能保证函数根据对应关系所计算的返回值因为状态的变化仍然保持不变。

这里单独讲解一下数据不可变,在JavaScript中有很多数组操作的方法,举个例子:

constarr=[1,2,3];console.log(arr.slice(0,2));//[1,2]console.log(arr);//[1,2,3]console.log(arr.slice(0,2));//[1,2]console.log(arr);//[1,2,3]console.log(arr.splice(0,1));//[1]console.log(arr);//[2,3]console.log(arr.splice(0,1));//[2]console.log(arr);//[3]复制代码这里的slice方法多次调用都不会改变原有数组,且会产生相同的输出。而splice每次调用都在修改原数组,且产生的输出也不相同。在函数式编程中,这种会改变原有数据的函数已经不再是纯函数,应该尽量避免使用。

阅读链接:如果想要了解更深入的函数式编程知识点,可以额外阅读函数式编程指北[8]。

ThedirectionCSSpropertysetsthedirectionoftext,tablecolumns,andhorizontaloverflow.Usertlforlanguageswrittenfromrighttoleft(likeHebreworArabic),andltrforthosewrittenfromlefttoright(likeEnglishandmostotherlanguages).

具体查看:developer.mozilla.org/en-US/docs/…[9]

Theconsoleobjectprovidesaccesstothebrowser'sdebuggingconsole(e.g.theWebconsole[10]inFirefox).Thespecificsofhowitworksvariesfrombrowsertobrowser,butthereisadefactosetoffeaturesthataretypicallyprovided.

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

具体查看:developer.mozilla.org/en-US/docs/…[11]

在JavaScript中利用事件循环机制[12](EventLoop)可以在单线程中实现非阻塞式、异步的操作。例如

我们重点来看一下常用的几种编程方式(Callback、Promise、Generator、Async)在语法糖上带来的优劣对比。

Callback

Callback(回调函数)是在Web前端开发中经常会使用的编程方式。这里举一个常用的定时器示例:

exportinterfaceIObj{value:string;deferExec():void;deferExecAnonymous():void;console():void;}exportconstobj:IObj={value:'hello',deferExecBind(){//使用箭头函数可达到一样的效果setTimeout(this.console.bind(this),1000);},deferExec(){setTimeout(this.console,1000);},console(){console.log(this.value);},};obj.deferExecBind();//helloobj.deferExec();//undefined复制代码回调函数经常会因为调用环境的变化而导致this的指向性变化。除此之外,使用回调函数来处理多个继发的异步任务时容易导致回调地狱(CallbackHell):

fs.readFile(fileA,'utf-8',function(err,data){fs.readFile(fileB,'utf-8',function(err,data){fs.readFile(fileC,'utf-8',function(err,data){fs.readFile(fileD,'utf-8',function(err,data){//假设在业务中fileD的读写依次依赖fileA、fileB和fileC//或者经常也可以在业务中看到多个HTTP请求的操作有前后依赖(继发HTTP请求)//这些异步任务之间纵向嵌套强耦合,无法进行横向复用//如果某个异步发生变化,那它的所有上层或下层回调可能都需要跟着变化(比如fileA和fileB的依赖关系倒置)//因此称这种现象为回调地狱//....});});});});复制代码回调函数不能通过return返回数据,比如我们希望调用带有回调参数的函数并返回异步执行的结果时,只能通过再次回调的方式进行参数传递:

//希望延迟3s后执行并拿到结果functiongetAsyncResult(result:number){setTimeout(()=>{returnresult*3;},1000);}//尽管这是常规的编程思维方式constresult=getAsyncResult(3000);//但是打印undefinedconsole.log('result:',result);functiongetAsyncResultWithCb(result:number,cb:(result:number)=>void){setTimeout(()=>{cb(result*3);},1000);}//通过回调的形式获取结果getAsyncResultWithCb(3000,(result)=>{console.log('result:',result);//9000});复制代码对于JavaScript中标准的异步API可能无法通过在外部进行try...catch...的方式进行错误捕获:

try{setTimeout(()=>{//下述是异常代码//你可以在回调函数的内部进行try...catch...console.log(a.b.c)},1000)}catch(err){//这里不会执行//进程会被终止console.error(err)}复制代码上述示例讲述的都是JavaScript中标准的异步API,如果使用一些三方的异步API并且提供了回调能力时,这些API可能是非受信的,在真正使用的时候会因为执行反转(回调函数的执行权在三方库中)导致以下一些问题:

举个简单的例子:

interfaceILib{params:T;emit(params:T):void;on(callback:(params:T)=>void):void;}//假设以下是一个三方库,并发布成了npm包exportconstlib:ILib={params:'',emit(params){this.params=params;},on(callback){try{//callback回调执行权在lib上//lib库可以决定回调执行多次callback(this.params);callback(this.params);callback(this.params);//lib库甚至可以决定回调延迟执行//异步执行回调函数setTimeout(()=>{callback(this.params);},3000);}catch(err){//假设lib库的捕获没有抛出任何异常信息}},};//开发者引入lib库开始使用lib.emit('hello');lib.on((value)=>{//使用者希望on里的回调只执行一次//这里的回调函数的执行时机是由三方库lib决定//实际上打印四次,并且其中一次是异步执行console.log(value);});lib.on((value)=>{//下述是异常代码//但是执行下述代码不会抛出任何异常信息//开发者无法感知自己的代码设计错误console.log(value.a.b.c)});复制代码Promise

Callback的异步操作形式除了会造成回调地狱,还会造成难以测试的问题。ES6中的Promise(基于PromiseA+[21]规范的异步编程解决方案)利用有限状态机[22]的原理来解决异步的处理问题,Promise对象提供了统一的异步编程API,它的特点如下:

温馨提示:有限状态机提供了一种优雅的解决方式,异步的处理本身可以通过异步状态的变化来触发相应的操作,这会比回调函数在逻辑上的处理更加合理,也可以降低代码的复杂度。

Promise对象的执行状态不可变示例如下:

constpromise=newPromise((resolve,reject)=>{//状态变更为fulfilled并返回结果1后不会再变更状态resolve(1);//不会变更状态reject(4);});promise.then((result)=>{//在ES6中Promise的then回调执行是异步执行(微任务)//在当前then被调用的那轮事件循环(EventLoop)的末尾执行console.log('result:',result);}).catch((error)=>{//不执行console.error('error:',error);});复制代码假设要实现两个继发的HTTP请求,第一个请求接口返回的数据是第二个请求接口的参数,使用回调函数的实现方式如下所示(这里使用setTimeout来指代异步请求):

//回调地狱constdoubble=(result:number,callback:(finallResult:number)=>void)=>{//Mock第一个异步请求setTimeout(()=>{//Mock第二个异步请求(假设第二个请求的参数依赖第一个请求的返回结果)setTimeout(()=>{callback(result*2);},2000);},1000);};doubble(1000,(result)=>{console.log('result:',result);});复制代码温馨提示:继发请求的依赖关系非常常见,例如人员基本信息管理系统的开发中,经常需要先展示组织树结构,并默认加载第一个组织下的人员列表信息。

如果采用Promise的处理方式则可以规避上述常见的回调地狱问题:

constfirstPromise=(result:number):Promise=>{returnnewPromise((resolve,reject)=>{//Mock异步请求//将resolve改成reject会被catch捕获setTimeout(()=>resolve(result),1000);});};constnextPromise=(result:number):Promise=>{returnnewPromise((resolve,reject)=>{//Mock异步请求//将resolve改成reject会被catch捕获setTimeout(()=>resolve(result*2),1000);});};firstPromise(1000).then((result)=>{returnnextPromise(result);}).then((result)=>{//2s后打印2000console.log('result:',result);})//任何一个Promise到达rejected状态都能被catch捕获.catch((err)=>{console.error('err:',err);});复制代码Promise的错误回调可以同时捕获firstPromise和nextPromise两个函数的rejected状态。接下来考虑以下调用场景:

constfirstPromise=(result:number):Promise=>{returnnewPromise((resolve,reject)=>{//Mock异步请求setTimeout(()=>resolve(result),1000);});};constnextPromise=(result:number):Promise=>{returnnewPromise((resolve,reject)=>{//Mock异步请求setTimeout(()=>resolve(result*2),1000);});};firstPromise(1000).then((result)=>{nextPromise(result).then((result)=>{//后打印console.log('nextPromiseresult:',result);});}).then((result)=>{//先打印//由于上一个then没有返回值,这里打印undefinedconsole.log('firstPromiseresult:',result);}).catch((err)=>{console.error('err:',err);});复制代码首先Promise可以注册多个then(放在一个执行队列里),并且这些then会根据上一次返回值的结果依次执行。除此之外,各个Promise的then执行互不干扰。我们将示例进行简单的变换:

constfirstPromise=(result:number):Promise=>{returnnewPromise((resolve,reject)=>{//Mock异步请求setTimeout(()=>resolve(result),1000);});};constnextPromise=(result:number):Promise=>{returnnewPromise((resolve,reject)=>{//Mock异步请求setTimeout(()=>resolve(result*2),1000);});};firstPromise(1000).then((result)=>{//返回了nextPromise的then执行后的结果returnnextPromise(result).then((result)=>{returnresult;});})//接着nextPromise的then执行的返回结果继续执行.then((result)=>{//2s后打印2000console.log('nextPromiseresult:',result);}).catch((err)=>{console.error('err:',err);});复制代码上述例子中的执行结果是因为then的执行会返回一个新的Promise对象,并且如果then执行后返回的仍然是Promise对象,那么下一个then的链式调用会等待该Promise对象的状态发生变化后才会调用(能得到这个Promise处理的结果)。接下来重点看下Promise的错误处理:

constpromise=newPromise((resolve,reject)=>{//下述是异常代码console.log(a.b.c);resolve('hello');});promise.then((result)=>{console.log('result:',result);})//去掉catch仍然会抛出错误,但不会退出进程终止脚本执行.catch((err)=>{//执行//ReferenceError:aisnotdefinedconsole.error(err);});setTimeout(()=>{//继续执行console.log('helloworld!');},2000);复制代码从上述示例可以看出Promise的错误不会影响其他代码的执行,只会影响Promise内部的代码本身,因为Promise会在内部对错误进行异常捕获,从而保证整体代码执行的稳定性。Promise还提供了其他的一些API方便多任务的执行,包括

Promise相对于Callback对于异步的处理更加优雅,并且能力也更加强大,但是也存在一些自身的缺点:

温馨提示:手写Promise是面试官非常喜欢的一道笔试题,本质是希望面试者能够通过底层的设计正确了解Promise的使用方式,如果你对Promise的设计原理不熟悉,可以深入了解一下或者手动设计一个。

Generator

Promise解决了Callback的回调地狱问题,但也造成了代码冗余,如果一些异步任务不支持Promise语法,就需要进行一层Promise封装。Generator将JavaScript的异步编程带入了一个全新的阶段,它使得异步代码的设计和执行看起来和同步代码一致。Generator使用的简单示例如下:

constfirstPromise=(result:number):Promise=>{returnnewPromise((resolve,reject)=>{setTimeout(()=>resolve(result*2),1000);});};constnextPromise=(result:number):Promise=>{returnnewPromise((resolve,reject)=>{setTimeout(()=>resolve(result*3),1000);});};//在Generator函数里执行的异步代码看起来和同步代码一致function*gen(result:number):Generator,Promise,number>{//异步代码constfirstResult=yieldfirstPromise(result)console.log('firstResult:',firstResult)//2//异步代码constnextResult=yieldnextPromise(firstResult)console.log('nextResult:',nextResult)//6returnnextPromise(firstResult)}constg=gen(1)//手动执行Generator函数g.next().value.then((res:number)=>{//将firstPromise的返回值传递给第一个yield表单式对应的firstResultreturng.next(res).value}).then((res:number)=>{//将nextPromise的返回值传递给第二个yield表单式对应的nextResultreturng.next(res).value})复制代码通过上述代码,可以看出Generator相对于Promise具有以下优势:

next可以不停的改变状态使得yield得以继续执行的代码可以变得非常有规律,例如从上述的手动执行Generator函数可以看出,完全可以将其封装成一个自动执行的执行器,具体如下所示:

需要注意的是Generator函数的返回值是一个Iterator遍历器对象,具体如下所示:

constfirstPromise=(result:number):Promise=>{returnnewPromise((resolve,reject)=>{setTimeout(()=>resolve(result*2),1000);});};constnextPromise=(result:number):Promise=>{returnnewPromise((resolve,reject)=>{setTimeout(()=>resolve(result*3),1000);});};typeGen=Generator>;function*gen():Gen{yieldfirstPromise(1);yieldnextPromise(2);}//注意使用next是继发执行,而这里是并发执行Promise.all([...gen()]).then((res)=>{console.log('res:',res);});for(constpromiseofgen()){promise.then((res)=>{console.log('res:',res);});}复制代码Generator函数的错误处理相对复杂一些,极端情况下需要对执行和Generator函数进行双重错误捕获,具体如下所示:

constfirstPromise=(result:number):Promise=>{returnnewPromise((resolve,reject)=>{//需要注意这里的reject没有被捕获setTimeout(()=>reject(result*2),1000);});};constnextPromise=(result:number):Promise=>{returnnewPromise((resolve,reject)=>{setTimeout(()=>resolve(result*3),1000);});};typeGen=Generator>;function*gen():Gen{try{yieldfirstPromise(1);yieldnextPromise(2);}catch(err){console.error('Generator函数错误捕获:',err);}}try{constg=gen();g.next();//返回Promise后还需要通过Promise.prototype.catch进行错误捕获g.next();//Generator函数错误捕获g.throw('err');//执行器错误捕获g.throw('err');}catch(err){console.error('执行错误捕获:',err);}复制代码在使用g.throw的时候还需要注意以下一些事项:

Async

Async是Generator函数的语法糖,相对于Generator而言Async的特性如下:

举个简单的示例:

constfirstPromise=(result:number):Promise=>{returnnewPromise((resolve,reject)=>{setTimeout(()=>resolve(result*2),1000);});};constnextPromise=(result:number):Promise=>{returnnewPromise((resolve,reject)=>{setTimeout(()=>resolve(result*3),1000);});};asyncfunctionco(){constfirstResult=awaitfirstPromise(1);//1s后打印2console.log('firstResult:',firstResult);//等待firstPromise的状态发生变化后执行constnextResult=awaitnextPromise(firstResult);//2s后打印6console.log('nextResult:',nextResult);returnnextResult;}co();co().then((res)=>{console.log('res:',res);//6});复制代码通过上述示例可以看出,async函数的特性如下:

上述代码是阻塞式执行,nextPromise需要等待firstPromise执行完成后才能继续执行,如果希望两者能够并发执行,则可以进行下述设计:

constfirstPromise=(result:number):Promise=>{returnnewPromise((resolve,reject)=>{setTimeout(()=>resolve(result*2),1000);});};constnextPromise=(result:number):Promise=>{returnnewPromise((resolve,reject)=>{setTimeout(()=>resolve(result*3),1000);});};asyncfunctionco(){returnawaitPromise.all([firstPromise(1),nextPromise(1)]);}co().then((res)=>{console.log('res:',res);//[2,3]});复制代码除了使用Promise自带的并发执行API,也可以通过让所有的Promise提前并发执行来处理:

constfirstPromise=(result:number):Promise=>{returnnewPromise((resolve,reject)=>{console.log('firstPromise');setTimeout(()=>resolve(result*2),10000);});};constnextPromise=(result:number):Promise=>{returnnewPromise((resolve,reject)=>{console.log('nextPromise');setTimeout(()=>resolve(result*3),1000);});};asyncfunctionco(){//执行firstPromiseconstfirst=firstPromise(1);//和firstPromise同时执行nextPromiseconstnext=nextPromise(1);//等待firstPromise结果回来constfirstResult=awaitfirst;console.log('firstResult:',firstResult);//等待nextPromise结果回来constnextResult=awaitnext;console.log('nextResult:',nextResult);returnnextResult;}co().then((res)=>{console.log('res:',res);//3});复制代码Async的错误处理相对于Generator会更加简单,具体示例如下所示:

constfirstPromise=(result:number):Promise=>{returnnewPromise((resolve,reject)=>{//Promise决断错误setTimeout(()=>reject(result*2),1000);});};constnextPromise=(result:number):Promise=>{returnnewPromise((resolve,reject)=>{setTimeout(()=>resolve(result*3),1000);});};asyncfunctionco(){constfirstResult=awaitfirstPromise(1);console.log('firstResult:',firstResult);constnextResult=awaitnextPromise(1);console.log('nextResult:',nextResult);returnnextResult;}co().then((res)=>{console.log('res:',res);}).catch((err)=>{console.error('err:',err);//err:2});复制代码async函数内部抛出的错误,会导致函数返回的Promise对象变为rejected状态,从而可以通过catch捕获,上述代码只是一个粗粒度的容错处理,如果希望firstPromise错误后可以继续执行nextPromise,则可以通过try...catch...在async函数里进行局部错误捕获:

业务思考更多的是结合基础知识的广度和深度进行的具体业务实践,主要包含以下几个方面:

笔试更多的是考验应聘者的逻辑思维能力和代码书写风格,主要包含以下几个方面:

THE END
1.操作系统分类操作系统有哪些操作系统分类 桌面操作系统:Windows、macOS、Linux 服务器操作系统:Linux(安全稳定免费)、Windows Server(付费、占有率低) 嵌入式操作系统:Linux(文件小) 应用:机顶盒、网络电视、程控交换机、手机PDA 移动设备操作系统:ios、Andriodhttps://blog.csdn.net/qq_44278313/article/details/124115906
2.常见的操作系统有哪些类型?常见的五大操作系统类型详细介绍微软还开发了适合服务器的操作系统,像windows server 2000,windows server 2003.一般的台式机不会去装此类的操作系统,因为最初的设计是为服务器安装的,个硬件的要求都不一样的。 2、UNIX操作系统 UNIX基本都是安装在服务器上,没有用户界面,基本上都是命令操作。所以你进入该系统的时候就是一个黑乎乎的界面,然后就https://www.jb51.net/os/other/815947.html
3.操作系统有哪些?全面解析主流操作系统及其应用嘲操作系统是现代计算机和移动设备的核心,选择合适的操作系统可以显著提升用户的工作和生活效率。Windows、macOS、Linux、Android和iOS各有其独特的优势和应用场景,用户应根据自身需求进行选择。希望本文能够帮助读者更全面地了解主流操作系统及其应用场景,从而做出更明智的选择。 下载地址 (学会使用小白系统工具,让你的生活多https://www.163987.com/jiaocheng/151927.html
4.操作系统有哪些Windows XP是许多用户刚开始接触电脑时所用的操作系统,从它开始慢慢的掌握了电脑的操作,好不容易已经操作熟练了,又来一个新的操作系统,许多用户就有抵触的情绪,干嘛要换,旧版的用的挺好的,新的`我又不会用,用的很不习惯也,换了新的有得重新学习,凭啥呀,不爽。 https://m.oh100.com/kaoshi/caozuoxitong/628831.html
5.操作系统有哪些操作系统都有哪些电脑知识常见的操作系统有”windows“、”macos“、”linux“、”android“、”ios“和”unix“六种:1、windows操作系统,由微软公司开发的操作系统;2、macos操作系统,由苹果公司开发的操作系统,用于苹果的mac电脑和笔记本电脑;3、linux操作系统,开源的、免费的操作系统,具有高度的定制性和安全性;4、android操作系统,由谷歌公司https://m.php.cn/faq/642247.html
6.思科网络技术学院教程(第6版):网络简介第2章“配置网络操作系统”:简要地介绍大多数思科设备使用的操作系统—Cisco IOS。描述IOS的基本用途和功能以及访问IOS的方法。这一章还将演示IOS命令行界面的用法以及IOS设备的基本配置。 第3章“网络协议和通信”:探讨规则或协议对网络通信的重要性。将探索OSI参考模型和TCP/IP通信簇,以及这些模型如何提供必要的协议https://www.epubit.com/bookDetails?id=N15003
7.一文速览计算机的前世今生表1 计算机早期发展重点列举 Part 04 照向未来 了解计算机发展史有何意义呢? 一方面,千变万变,底层不变。 算力网络、智慧家庭、元宇宙,是近年来较热门的事物,它们较大程度上是基于现有技术进行整合创新,如云计算、物联网等等,而这二者归根结底是CPU、内存、操作系统、网络等等在强力支撑。了解根源,透过现象看本质https://www.51cto.com/article/746805.html
8.把这些计算机基础知识学完后,我飘了!Windows 操作系统的特征 Windows 操作系统是世界上用户数量最庞大的群体,作为 Windows 操作系统的资深用户,你都知道 Windows 操作系统有哪些特征吗?下面列举了一些 Windows 操作系统的特性 Windows 操作系统有两个版本:32位和64位 通过API函数集成来提供系统调用 https://maimai.cn/article/detail?fid=1400649709&efid=VmhIiOBVBH0ua86U1tJoEA
9.西藏农牧学院网络安全宣传周专栏如丢失密码、疏忽大意、非法操作等都可以对网络造成极大的破坏。 3 系统的漏洞和陷门 操作系统和网络软件不可能是完美无缺的 而这些漏洞或者陷门正好就给黑 客提供了一个入口 成为他们的首选攻击目标。而大部分的黑客攻入网络内部 都是因为安全措施不完善所致的。 http://www.xza.edu.cn/wlaqxcz/News_View.asp?NewsID=484
10.收银工作计划(精选20篇)1、监督夜班对商场的门,关闭和夜间红外报警系统操作工作。 2、对夜班值班情况每周进行二次以上的突击检查。 收银工作计划 2 新的一年到来了,为了在新的一年能够把工作做到最好,特制定工作计划如下: 一、全面提高个人业务技能 除本部门业务技能外,多学习营运方面知识,了解公司的经营状况及未来发展方向,确立目标,不局https://www.unjs.com/fanwenwang/gzjh/20220119165705_4691471.html
11.中标麒麟高级服务器操作系统系统管理员手册中标麒麟高级服务器操作系统-系统管理员手册.pdf,中标软件 中标麒麟高级服务器操作系统(龙芯) 系统管理员手册 目录 1 基本系统设置 8 1 . 1 系统地区和键盘配置8 1.1.1 配置系统地区 8 1.1.2 其他资源10 1 . 2 日期和时间配置 10 1.2.1 T IMEDAT ECT L 工具使用说明 10 1.https://m.book118.com/html/2022/0705/8026042070004115.shtm
12.操作系统(豆瓣)本书既是关于操作系统概念、结构和机制的教材,目的是尽可能清楚和全面地展示现代操作系统的本质和特点;也是讲解操作系统的经典教材,不仅系统地讲述了操作系统的基本概念、原理和方法,而且以当代流行的操作系统Windows 8、UNIX、Android、Linux为例,展现了当代操作系统的本质和特点。全书共分背景知识、进程、内存、调度、https://book.douban.com/subject/26993995/
13.列举你所知道的计算机操作系统,手机有哪些操作系统系统换了 https://ask.zol.com.cn/x/6550534.html
14.恶意代码分析实战第七章分析恶意Windows程序Autoruns工具列举在操作系统启动时会自动启动运行的代码 常用注册表函数 恶意代码常使用一些注册表函数来操作注册表,以便可以实现开机自启动,下面是常见的注册表。 RegOpenKeyEx打开一个注册表进行编辑和查询。有些函数允许你查询和编辑-一个注册表键,而不用先打开它,但是大多数程序还是会先使用RegOpenKeyEx。 https://www.jianshu.com/p/845cbfb96077
15.开源嵌入式操作系统和开源手机操作系统ERP优易软件它最初是自由软件基金会为其GNU操作系统所写,但目前最主要的应用是配合Linux内核,成为GNU/Linux操作系统一个重要的组成部分。 在通用的PC和Server中,Linux(ubuntu, Redhat, CentOS etc.)默认提供对glibc的支持;但是在嵌入式应用中,考虑到系统对os大小的要求和简化系统的复杂度等因素,并不一定支持glibc,而是支持http://www.uesoft.com/forum.php?mod=viewthread&tid=15821
16.电力安全的论文网络信息技术的信息共享的属性和基本前提决定了一定的安全漏洞会存在于电力系统的各个计算机网络信息系统中。网络协议漏洞也是危害电力系统网络信息安全的重要因素之一。 2.3工作环境 时下众多电力企业的计算机操作系统以及相关数据库系统等工作环境存在诸如网路通信协议错误以及存在于自身体系中的安全漏洞等等诸多不确定因素带来https://www.ruiwen.com/lunwen/6833724.html