营销型企业网站建设教案,车载互联系统网站建设,网站建设的公司选择哪家好,安徽池州网站制作Typescript第八章 异步编程#xff0c;并发和并行
异步API#xff0c;比如说回调#xff0c;promise和流。
JavaScript引擎在一个线路中多路复用任务#xff0c;而其他任务则处于空闲状态。这种事件循环是JavaScript引擎的标准线程模型。 多路复用是指在一个线程中同时处…Typescript第八章 异步编程并发和并行
异步API比如说回调promise和流。
JavaScript引擎在一个线路中多路复用任务而其他任务则处于空闲状态。这种事件循环是JavaScript引擎的标准线程模型。 多路复用是指在一个线程中同时处理多个任务 异步编程让程序难以理解。我们不能一行一行的分析程序。
Typescript提供了工具通过类型可以追踪异步操作借助内置的async/await可以把熟悉的同步思想运用到异步程序上。使用Typescript还可以为多线程程序指定严格的的消息传递协议。
我们先讨论一下JavaScript引擎中具体是如何运作的弄清楚为什么看似单个的线程中可以暂停和恢复执行。
8.1 JavaScript的事件循环
下面代码怎么执行 setTimeout(()console.log(A),1)// 时间到了就加入事件队列等待执行setTimeout(()console.log(B),2)console.log(C);JavaScript和C语言中sleep才用的并发模型以及java把作业调度到另外一个线程中不同。
概括的说JavaScriptVM采用下述方式模拟并发
JavaScript主线程调用XMLHTTPRequest处理Ajax请求setTimeoutreadFile等异步API。这些API由JavaScript平台提供我们自己不能创建调用原生的异步API之后控制权返回主线程继续向下执行就像从未调用异步API一样。异步操作执行完毕后平台在事件队列中添加一个任务。每个线程都有自己的队列异步操作的结果就通过队列发回主线程。任务中有关于调用的元信息还有主线程中回调函数的引用。主线程的调用堆栈清空后平台将检查事件队列中有没有待处理的任务。如果有等待处理的任务平台着手处理触发一个函数调用把控制权返还给主线程中的那个函数。调用那个函数之后如果调用堆栈又变空了平台再次检查事件队列中有没有可以处理的任务了。这个循环一直运转下去直到调用栈和事件队列都空而且所有原生的异步API调用都已结束。
8.2 处理回调
JavaScript异步程序的核心基础就是回调。回调其实就是常规的函数只是作为参数传给另一个函数。就像在同步程序中一样另一个函数在做完操作处理网络请求等之后调用回调函数。异步代码调用的回调也是函数而且类型签名中没有标明函数是异步调用的。
NodeJS的原生API。例如fs.readFile(采用异步方式从磁盘中读取文件的内容)和dns.resolveCname采用异步方式解析CNAME记录按照约定回调的第一个参数是错误或null第二个参数是结果或null。错误优先。
readFile的类型签名如下 function readFile(path:string,options:{encoding:string,flag?:string},callback:(err:Error|null,data:string|null)void):void{}注意readFile和callback的类型没有什么特别之处都是常规的JavaScript函数。签名中没有特别标明readFile是异步的也没有指出在调用readFile之后控制权会立即传给下一行代码不等待readFile的结果。 pnpm add types/node -D安装nodejs的类型声明 下面写一个案例读取文件 import * as fs from fsfs.readFile(log.txt,{encoding:utf-8},(error,data){if(error){console.log(error);return}console.log(success!:data);})// 采用并发写入fs.appendFile(log.txt,笑死我了,error{if(error){console.log(errorerror);}})这里API是异步的不能把API在代码中调用顺序理解为执行文件系统的操作顺序。readFile虽然在前面调用但是读取出来的访问日志可能没有后面新增的那行日志具体有没有要看运行这段代码时文件系统有多繁忙。
Nodejs一般约定如果函数的最后一个参数是一个接受两个参数的函数而且顺序为Error|null和T|null类型那么这个函数通常是异步的。
无论如何从类型上是看不出来的。
回调函数容易产生一个问题回调地狱 setTimeout((){console.log(1);setTimeout((){console.log(2);setTimeout((){console.log(3);console.log(啊);},1)},1)},1)按顺序执行的操作往往是一环扣一环前一步成功才执行下一步除非遇到错误我们要自己手动维护执行的顺序。按一定顺序执行的回调很容易出错。
有序的操作只是我们想借助异步任务执行的一种操作此外我们可能还想并行运行几个函数获知全部函数何时运行完毕或者让几个函数竞争只获取第一个结束的函数返回结果等。
使用回调函数可执行简单的异步任务虽然回调适合处理简单的任务但是如果异步任务变多很容易一团乱麻。
8.3 promise(我tm来了)
我们不是第一批遇到这些限制的程序员。本节说明promise这个概念对异步进行抽象方便任务编排排列任务等。即使你以前使用过promise或future也能更好的理解他们的原理。 // 自行分析console.log(start);async function async1() {console.log(async1);let result await async2()console.log(result);}async function async2() {console.log(async2);return Promise.resolve(wohaole)}async1()console.log(end);下面举个例子指出我们想如何使用Promise先向文件中添加一些内容然后再把文件中的内容读取出来 function appendAndReadPromise(path:string,data:string):Promisestring{return appendPromise(path,data).then(()readPromise(path)).catch(errorconsole.log(error);)}注意这里没有回调地狱我们把想执行的一些异步任务变成了易于理解的线性链条前一个任务完成后才能执行下一个任务倘若失败则跳到catch子句。假如是基于回调的API那么写出的代码可能是下面这样 function appendAndRead(path: string,data: string,cb: (error: Error | null, result: string | null | any) void) {appendFile(path, data, err {if (err) {return cb(err, null)}readFile(path, (err, result) {if (err) {return cb(err, null)}cb(null, result)})})}针对这个设想我们手动实现PromiseAPI
new Promise接受一个函数我们称为执行器executor。在Promise的实现中执行器接受两个参数一个是resolve函数一个是reject函数。 type Executor (resolve: Function,reject: Function) voidclass Promise {constructor(f:Executor){}}那么resolve和reject是如何运行的呢下面通过示例说明一下。假设我们把Nodejs中的一个回调API(例如fs.readFile)改造成基于Promise的APINodejs内置的fs.readFileAPI时这样使用的 import {readFile} from fsreadFile(path,(error,result){//..})import {readFile} from fsfunction readFilePromise(path:string):Promisestring{return new Promise((resolve,reject){readFile(path,(err,result){if(err){reject(err)}resolve(result)})})}可见resolve的参数是什么类型取决于具体使用的API这里其参数的类型就是result的类型而reject的参数始终是Error类型。因此我们要更行类型把不安全的Function改为更具体的类型 type ExecutorT,E extends Error (resolve: (result:T)void,reject: (error:E)void) voidclass PromiseT,E extends Error {constructor(f:ExecutorT,E){}}class PromiseT,E extends Error {constructor(f:ExecutorT,E){}thenU,F extends Error(g:(result:T)PromiseU,F):PromiseU,F{//}catchU,F extends Error(g:(error:E)PromiseU,F):PromiseU,F{//}}then和catch以不同的方式排列Promisethen把成功从一个Promise获得的结果映射到一个新Promise上catch则把错误映射到一个新的Promise上从被拒绝的状态中走出去。这种风格和上节中的Option设计模式一样都受函数式编程语言Haskell中的Monad设计模式影响
then的使用方法 let a:()Promisestring,TypeError //..let b:(s:string)Promisenumber,never //let c:()Promiseboolean,RangeError//a().then(b).catch(ec()).then(resultconsole.info(Done,result)).catch(econsole.log(error,e))此外我们还有处理Promise真正抛出异常的情况例如throw Error(‘foo’)),为此在实现then和catch要把代码放在try/catch中在cattch中分支处理被拒绝的情况然后事情并没有那么简单这里还涉及一些其他问题
Promise对象都有可能被拒而通过静态检查发现不了这个问题。Promise对象被拒不一定是因为有错。Typescript别无选择只能继承JavaScript的行为而在JavaScript中通过throw可以抛出一切。
考虑这两点我们要放宽对Promise类型的要求不指定错误类型 type ExecutorT (resolve: (result: T) void,reject: (error: unknown) void) voidclass PromiseT {constructor(f: ExecutorT) { }thenU(g: (result: T) PromiseU): PromiseU {// }catchU(g: (error: unknown) PromiseU): PromiseU{// }}let a new Promise((resolve,reject){resolve(123)})a.then((result){// })至此Promise封装好了内部具体实现自行研究。
8.4 async和await
promise对于异步代码所做的抽象十分强大JavaScript自身当然包括Typescript也有相应的句法async和awai使用这种句法可以像同步操作那样处理异步操作 await可以视为.then在语言上的语法糖。使用await处理Promise对象时要把相关的代码放在async块中。这种情况下不再使用.catch,而是把await放在常规的try/catch中。 // 老写法function getUser(){getUserId(18).then(usergetLocation(user)).then(locationconsole.log(location)).catch(errorconsole.log(error)).finally(()console.log(done))}async function getUser() {try {let user await getUserId(18)let location await getLocation(user)console.log(location);} catch (error) {console.log(error);} finally{console.log(done);}}async和await是JavaScript特性这里就不深入探究了。
8.5 异步流
promise对象是便于建模排列和编排未来的值但是如果有多个值在未来的不同时刻产出。这种情况并不少见比如从文件系统中读取文。
这样的操作有不同的建模方式最为常见的是事件发射器Nodejs EventEmitter或响应式编程库RxJS。这两种方式之间的区别就像回调和promise对象一样事件简单轻量而响应式编程库更强大可以编排和排列事件流。
事件发射器
事件发射器提供的API用于在通道中发射事件并监控通道中的事件 interface Emitter{emit(channel:string,value:unknown):voidon(channel:string,f:(value:string)void):void}发射器是JavaScript中一种常见的设计模式使用DOM事件或Nodejs EventEmitter模块中可能涉及。 type RedisClient {onE extends keyof Events(event:E,f:(arg:Events[E])void):voidemitE extends keyof Events(event:E,arg:Events[E]):void}type RedisClient {on(event:ready,f:()void):voidon(event:error,f:(e:Error)void):voidon(event:reconnecting,f:(params:{attempt:number,delay:number})void):void}// 优化type Events {ready:voiderror:Errorreconnecting:{attempt:number,delay:number}}type RedisClient {onE extends keyof Events(event:E,f:(arg:Events[E])void):void}把事件名称和参数提取到结构中然后映射该结构生成监听器和发射器这是Typescript中常见的模式。
8.6 多线程类型安全
目前我们讨论的异步程序基本上运行在一个CPU线程中不过一些CPU密集型任务可能需要并行把一项任务分到多个线程中。这么做可能是为了提升速度可能是想让主线程空闲出来继续相应后序操作。本节介绍编写安全的并行程序。涵盖浏览器和服务器。
8.6.1 在浏览器中使用Web职程
浏览器大都支持web职程worker处理多线程。未免某些操作例如CPU密集型任务阻塞主线程导致UI无响应我们可以在JavaScript主线程中创建一些职称严格受限的后后台代码而Promise和setTimeout等异步PAI只是并发运行代码。职程在另一个cpu中运行代码。web职程可以处理网络请求文件系统写入等操作不过有一些小限制。
Web职程是浏览器提供的API设计人员对安全性提出了更高的要求这里的“安全性”指的是内存安全。如果多个线程读取同一块内存很容易遇到各种并发问题。例如不确定性死锁。 在tsconfig.json中加入“lib”:[“dom”,”es20120”,”webworker”]支持webworker // 主线程代码let worker new Worker(./workerScript.js)worker.postMessage(some data)// 接受其他线程worker.onmessage e{console.log(e.data);}// 并行线程只能在浏览器环境才能用onmessage e{console.log(child,e.data);// 发给主线程 postMessage(receivere.data)}这样传递消息很简单但是没有类型无法确保正确处理可能发送的所有消息类型。
类型安全的协议
我们知道如何在两个线程之间传递消息那么若想指明一个命令始终收到特定事件的响应怎么做呢
我们可以选择在职程 中定义函数把参数发给该函数再把结果发送回来。
我们构建一个计算引擎让他支持三种运算求矩阵的行列式计算两个矩阵的点积和求逆矩阵。 type Matrix number[][]type MatrixProtocol {determinant:{in:[Matrix],out:number},dot-product:{in:[Matrix,Matrix]out:Matrix},invert:{in:[Matrix],out:Matrix}}我们将在主线程中定义矩阵运算则交给职程。我们对不安全的操作职程发送和接受不带类型的消息进行包装把带类型的API开放给使用方。我们定义一个简单请求响应协议Protocol列出职程可执行的操作并为预期的输入和输出声明类型。 type Protocol { // 1[command:string]:{in:unknown[],out:unknown}}function createProtocolP extends Protocol(script:string){ //2return K extends keyof P(command:K)(...args:P[K][in]){return new PromiseP[K][out]((resolve,reject){let worker new Worker(script)worker.onerror rejectworker.onmessage event resolve(event.data.data)worker.postMessage({command,args})})}}// let runWithMatrixProtocol createProtocolMatrixProtocol(MatricWorkerScript.js)let parallelDeterminant runWithMatrixProtocol(determinant)parallelDeterminant([[1,2],[3,4]]).then(determinantconsole.log(determinant))type Matrix number[][]type MatrixProtocol {determinant:{in:[Matrix],out:number},dot-product:{in:[Matrix,Matrix]out:Matrix},invert:{in:[Matrix],out:Matrix}}type Protocol { // 1[command:string]:{in:unknown[],out:unknown}}function createProtocolP extends Protocol(script:string){ //2return K extends keyof P(command:K){return (...args:P[K][in]){return new PromiseP[K][out]((resolve,reject){let worker new Worker(script)worker.onerror rejectworker.onmessage event resolve(event.data.data)worker.postMessage({command,args})})}}}// let runWithMatrixProtocol createProtocolMatrixProtocol(MatricWorkerScript.js)let parallelDeterminant runWithMatrixProtocol(determinant)parallelDeterminant([[1,2],[3,4]]).then(determinantconsole.log(determinant))8.6.2 在nodejs使用子进程 // mainimport {fork} from child_processlet child fork(./child.js)child.on(message,data{})child.send({type:syn,data:[3]})
文章转载自: http://www.morning.kqkmx.cn.gov.cn.kqkmx.cn http://www.morning.rrwgh.cn.gov.cn.rrwgh.cn http://www.morning.mcqhb.cn.gov.cn.mcqhb.cn http://www.morning.jwxmn.cn.gov.cn.jwxmn.cn http://www.morning.rkck.cn.gov.cn.rkck.cn http://www.morning.zlxkp.cn.gov.cn.zlxkp.cn http://www.morning.qfzjn.cn.gov.cn.qfzjn.cn http://www.morning.qwyms.cn.gov.cn.qwyms.cn http://www.morning.syssdz.cn.gov.cn.syssdz.cn http://www.morning.rwjfs.cn.gov.cn.rwjfs.cn http://www.morning.trkl.cn.gov.cn.trkl.cn http://www.morning.ubpsa.cn.gov.cn.ubpsa.cn http://www.morning.lmctj.cn.gov.cn.lmctj.cn http://www.morning.zckhn.cn.gov.cn.zckhn.cn http://www.morning.dztp.cn.gov.cn.dztp.cn http://www.morning.swwpl.cn.gov.cn.swwpl.cn http://www.morning.xyyplp.cn.gov.cn.xyyplp.cn http://www.morning.kwnnx.cn.gov.cn.kwnnx.cn http://www.morning.kjdxh.cn.gov.cn.kjdxh.cn http://www.morning.lwzgn.cn.gov.cn.lwzgn.cn http://www.morning.nkjnr.cn.gov.cn.nkjnr.cn http://www.morning.qphgp.cn.gov.cn.qphgp.cn http://www.morning.zlfxp.cn.gov.cn.zlfxp.cn http://www.morning.srnth.cn.gov.cn.srnth.cn http://www.morning.llyqm.cn.gov.cn.llyqm.cn http://www.morning.wjqyt.cn.gov.cn.wjqyt.cn http://www.morning.khtjn.cn.gov.cn.khtjn.cn http://www.morning.zrrgx.cn.gov.cn.zrrgx.cn http://www.morning.zdwjg.cn.gov.cn.zdwjg.cn http://www.morning.trbxt.cn.gov.cn.trbxt.cn http://www.morning.lwrcg.cn.gov.cn.lwrcg.cn http://www.morning.kgphc.cn.gov.cn.kgphc.cn http://www.morning.gqtxz.cn.gov.cn.gqtxz.cn http://www.morning.kqcqr.cn.gov.cn.kqcqr.cn http://www.morning.rjfr.cn.gov.cn.rjfr.cn http://www.morning.kxgn.cn.gov.cn.kxgn.cn http://www.morning.pqryw.cn.gov.cn.pqryw.cn http://www.morning.gqbtw.cn.gov.cn.gqbtw.cn http://www.morning.xfxnq.cn.gov.cn.xfxnq.cn http://www.morning.lnmby.cn.gov.cn.lnmby.cn http://www.morning.trsfm.cn.gov.cn.trsfm.cn http://www.morning.rldph.cn.gov.cn.rldph.cn http://www.morning.ktsth.cn.gov.cn.ktsth.cn http://www.morning.kqpxb.cn.gov.cn.kqpxb.cn http://www.morning.kfwrq.cn.gov.cn.kfwrq.cn http://www.morning.kyzxh.cn.gov.cn.kyzxh.cn http://www.morning.bloao.com.gov.cn.bloao.com http://www.morning.jlgjn.cn.gov.cn.jlgjn.cn http://www.morning.etsaf.com.gov.cn.etsaf.com http://www.morning.xhddb.cn.gov.cn.xhddb.cn http://www.morning.yrrnx.cn.gov.cn.yrrnx.cn http://www.morning.jqpyq.cn.gov.cn.jqpyq.cn http://www.morning.jnptt.cn.gov.cn.jnptt.cn http://www.morning.sjjq.cn.gov.cn.sjjq.cn http://www.morning.ndxss.cn.gov.cn.ndxss.cn http://www.morning.gwmjy.cn.gov.cn.gwmjy.cn http://www.morning.dybth.cn.gov.cn.dybth.cn http://www.morning.skbhl.cn.gov.cn.skbhl.cn http://www.morning.tklqs.cn.gov.cn.tklqs.cn http://www.morning.zkbxx.cn.gov.cn.zkbxx.cn http://www.morning.rqrh.cn.gov.cn.rqrh.cn http://www.morning.spfq.cn.gov.cn.spfq.cn http://www.morning.ttnfc.cn.gov.cn.ttnfc.cn http://www.morning.qwfq.cn.gov.cn.qwfq.cn http://www.morning.kbgzj.cn.gov.cn.kbgzj.cn http://www.morning.psdsk.cn.gov.cn.psdsk.cn http://www.morning.wcghr.cn.gov.cn.wcghr.cn http://www.morning.sqfrg.cn.gov.cn.sqfrg.cn http://www.morning.lbxcc.cn.gov.cn.lbxcc.cn http://www.morning.mmplj.cn.gov.cn.mmplj.cn http://www.morning.ynlpy.cn.gov.cn.ynlpy.cn http://www.morning.yzzfl.cn.gov.cn.yzzfl.cn http://www.morning.kpwcx.cn.gov.cn.kpwcx.cn http://www.morning.pwrkl.cn.gov.cn.pwrkl.cn http://www.morning.pcjw.cn.gov.cn.pcjw.cn http://www.morning.nnjq.cn.gov.cn.nnjq.cn http://www.morning.ggqcg.cn.gov.cn.ggqcg.cn http://www.morning.bwfsn.cn.gov.cn.bwfsn.cn http://www.morning.fktlr.cn.gov.cn.fktlr.cn http://www.morning.nlhcb.cn.gov.cn.nlhcb.cn