精华内容
下载资源
问答
  • 如何实现文件的同步/异步上传

    千次阅读 2019-07-24 15:02:22
    ">上传教学班信息</label> onclick="subimtBtn();" value="上传" class="put" /> method="post" onsubmit="return saveReport();"> <!-- target="nm_iframe"" --> ...

    (JS使用的是JQuery)

    前端页面这么写就够了

    
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>上传excle表</title>
    <script src="js/jquery-1.8.0.min.js"></script>
    <script type="text/javascript">
    	function saveReport() {
    		// jquery 表单提交 
    		$("#uploadForm").ajaxSubmit(function(message) {
    			// 对于表单提交成功后处理,message为提交页面saveReport.htm的返回内容 
    			alert(message);
    		});
    		return false; // 必须返回false,否则表单会自己再做一次提交操作,并且页面跳转 
    	}
    	function check(file) {
    		if (file.value == null || file.value == "") {
    			alert("请您选择要导入的Excel数据!");
    			return false;
    		}
    	}
    </script>
     
    </head>
    <body>
    	<div>
    		<form id="uploadForm" enctype="multipart/form-data" action="/student"
    			method="post" onsubmit="return saveReport();">
    			<!--  target="nm_iframe"" -->
    			<input class="file input" type="file" id="file" name="file"
    				multiple="multiple" /><label>上传学生信息</label> <input type="submit"
    				onclick="return check(file)" value="上传"
    				style="position: relative; top: 0px; left: 180px; height: 24px;" />
    			<!--  <button id="upload" type="button" onclick="picture();">上传</button>  -->
    		</form>
    		<!-- <iframe id="id_iframe" name="nm_iframe" style="display:none;"></iframe> -->
    	</div>
    	
     
    	<script src="js/jquery-form.js"></script>
    </body>
    </html>
    
    

    后台这么接收:

    
    
    @Controller
    public class WebStudentController {
    
    private static final Logger log = LoggerFactory.getLogger(WebStudentController.class);
    
    
    @RequestMapping("/student")
    @ResponseBody
    public String upload(@RequestParam("file1") MultipartFile[] file,HttpServletRequest request) {	
    
    //中间代码省略
    }
    }	
    

    异步上传:

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>上传excle表</title>
    <script src="js/jquery-1.8.0.min.js"></script>
    <!-- <script type="text/javascript">
    	function saveReport() {
    		// jquery 表单提交 
    		$("#uploadForm").ajaxSubmit(function(message) {
    			// 对于表单提交成功后处理,message为提交页面saveReport.htm的返回内容 
    			alert(message);
    		});
    		return false; // 必须返回false,否则表单会自己再做一次提交操作,并且页面跳转 
    	}
    	function check(file) {
    		if (file.value == null || file.value == "") {
    			alert("请您选择要导入的Excel数据!");
    			return false;
    		}
    	}
    	
    </script> -->
     
    <script type="text/javascript">
    function subimtBtn() { 
    	 var form = $("form[name=file]"); 
    	 var options = {  
    	 url:'/upload', //上传文件的路径  
    	 type:'post',
    	 success:function(data){  
    	  alert(data); 
    	  //....       //异步上传成功之后的操作
    	  }
    	 };  
    	 form.ajaxSubmit(options); 
    	} 
     
    function subimtBtn1() { 
    	 var form = $("form[name=file1]"); 
    	 var options = {  
    	 url:'/student', //上传文件的路径  
    	 type:'post',
    	 success:function(data){  
    	  alert(data);
    	  //....       //异步上传成功之后的操作
    	  }
    	 };  
    	 form.ajaxSubmit(options);  
    	} 
    function subimtBtn2() { 
    	 var form = $("form[name=file2]"); 
    	 var options = {  
    	 url:'/teacher', //上传文件的路径  
    	 type:'post',
    	 success:function(data){  
    		  alert(data);
    	  //....       //异步上传成功之后的操作
    	  }
    	 };  
    	 form.ajaxSubmit(options); 
    	} 
    </script>
    <style>
    	body{
    		background: #eefff2;
    		color: #00380c;
    	}
    	.box{
    		text-align:center;
    	}
    	/* .box span:nth-child(1){
    		margin-right: 48px;
    	}
    	.box span:nth-child(2){
    		margin-right: 32px;
    	} */
    	.tit{
    		font-size: 36px;
    		font-family: 楷体;
    		line-height: 90px;
    		border-bottom: #00C62D 3px solid;
    	}
    	.row{
    		line-height: 55px;
    		/* border-bottom: #00C62D 1px solid; */
    	} 
    	.put{
    		position: relative;
    		top: 0px;
    		left: 60px;
    		background: #83ff9f;
    		border: #00c62d 1px solid;
    		border-radius: 3px;
    		height: 24px; 
    		position: relative; 
    		top: 0px; 
    		left: 60px; 
    		height: 24px;
    	}
    	.put:hover{
    		box-shadow:inset #a4ada4eb 0 0 10px 0;
    		cursor:pointer;
    	}
    </style>
    </head>
    <body>
    	<div class="box">
    		<div class="tit">上传excle表</div> 
    		<div class="row">
    			<form id="uploadForm" enctype="multipart/form-data" action="" name="file"
    				method="post" > <!-- onsubmit="return saveReport();"  -->
    				<input class="file input" type="file" id="file" name="file"
    					multiple="multiple" /><label style="margin-right: 48px;">上传教学班信息</label> <input type="button"
    					onclick="subimtBtn();" value="上传"
    					class="put" />			
    			</form>
    		</div>
    		<div class="row">
    			<form id="uploadForm" enctype="multipart/form-data" action="" name="file1"
    				method="post" onsubmit="return saveReport();">
    				<!--  target="nm_iframe"" -->
    				<input class="file input" type="file" id="file" name="file1" multiple="multiple" /><label style="margin-right: 32px;">上传学生详细信息</label> 
    				<input type="button" onclick="subimtBtn1();" value="上传"
    				class="put"/>
    			</form>
    		</div>
    		<div class="row">
    			<form id="uploadForm" enctype="multipart/form-data" action="" name="file2"
    				method="post" onsubmit="return saveReport();">
    				<!--  target="nm_iframe"" -->
    				<input class="file input" type="file" id="file" name="file2" multiple="multiple" /><label>上传教师授课详细信息</label>
    				 <input type="button" onclick="subimtBtn2();" value="上传"
    				class="put" />
    			</form>
    		</div>
    	</div>
    	<script src="js/jquery-form.js"></script>
    </body>
    </html>
    

    参考文献:
    https://blog.csdn.net/qq_41790332/article/details/84349631

    展开全文
  • ,调查发现与程序进程中线程的同步异步有关。 问题描述: 使用文件查看器打开压缩文件时,程序没有响应。 问题原因: 1。经过调查发现,文件查看器打开压缩文件时,会遍历压缩文件中的所有资源,在遍历...

     

    案例描述

    在日常测试中,当程序加载数据文件时,经常会出现页面卡住、程序不响应、程序崩溃等情况。,调查发现与程序进程中线程的同步和异步有关。

     

    问题描述:

    使用文件查看器打开压缩文件时,程序没有响应。

    问题原因:

    1。经过调查发现,文件查看器打开压缩文件时,会遍历压缩文件中的所有资源,在遍历过程中,出现资源解析错误会导致任务阻塞。2.经过进一步了解,我们发现开发实现该功能时,资源遍历线程放入主线程同步加载,所以当资源遍历线程被阻塞时,主线程没有响应。

    解决方案:

    1。对资源遍历线程的阻塞场景增加保护,保证遍历线程的正常执行;2.将资源遍历压缩文件线程从主线程放到子线程,形成异步加载,避免单线程失败导致主线程直接挂机,影响程序使用;从以上案例可以看出,在逻辑实现中使用同步或异步线程对于提高程序效率和保护主线程非常重要。

     

    扩展思维

    1.开发层面的线程使用:1)使用线程的场景:①当手机APP程序模块需要加载框架+内容时,使用线程效率更高;②优化程序性能时,更倾向于使用线程;2)线程同步和异步在程序中的应用:①主线程执行任务时,异步任务在子线程中执行。主线程可以在主线程上完成操作,而不需要等待子线程的结果返回,然后调用回主线程完成整个过程。这是线程的异步加载操作,可以提高执行效率(如下图);

     

     

    ②当主线程正在执行任务1时,需要等待任务1的响应完成后才能启动任务2。如果任务1被阻止,整个过程将无法继续。这样的同步线程对执行效率影响很大(如下图所示)。

     

    2.线程对测试的影响:1)线程对用例设计的影响:1)数据流加载类的作用。这些功能大部分是异步线程,测试用例的设计可以从网络级和负载级来考虑。②JS注入类的功能。这些函数大部分都是同步线程,所以可以从注入JS函数的有效性和JS注入的时机来考虑测试用例的设计。2)线程对程序性能的影响:①从安全性和性能的角度来看,同步线程虽然解决了线程的安全问题,但始终存在判断锁,导致性能下降;②与同步线程相比,异步线程对程序性能有积极影响,效率更高。一般建议在APP程序中使用异步线程,以保证程序效率。

     

    注:在安全和性能之间,安全是第一考虑因素,其次是性能。

    理解程序实现的逻辑在测试工作中是必不可少的。只有更仔细、更清晰地了解,才能对检测方案有更全面的思考,从而保证产品质量。在这里,希望朋友们在技术上越来越好,在软件测试上越来越高!

    展开全文
  • 施耐德电气ATV71同步电机与异步电机变频器安装手册pdf,本文档是施耐德电气ATV71同步电机与异步电机变频器安装手册。1、本站所有软件及资料皆从互联网收集整理而来,仅供个人试用、教学和学习交流之用,请勿用于商业...
  • OkHttp源码解读总结(二)--->OkHttp同步/异步请求

    OkHttp源码解读总结(二)—>OkHttp同步/异步请求

    标签(空格分隔): OkHttp源码 学习笔记


    前言

    • 以下的相关知识总结是通过慕课网的相关学习和自己的相关看法,如果有需要的可以去查看一下慕课网的相关教学,感觉还可以。

    OkHttp同步请求

    同步请求简单步骤

      /**
         * 构建OkHttpClient对象
         *
         * @return OkHttpClient
         */
        public OkHttpClient getOkHttpClient() {
    
            return new OkHttpClient.Builder()
                    .connectTimeout(10000, TimeUnit.MILLISECONDS)
                    .readTimeout(10000, TimeUnit.MILLISECONDS)
                    .writeTimeout(10000, TimeUnit.MICROSECONDS)
                    .build();
        }
    
        /**
         * 同步请求
         */
        public void synRequest() {
    
            Request request = new Request.Builder().url("http://www.baidu.com")
                    .get()
                    .build();
    
            Call requestCall = getOkHttpClient().newCall(request);
    
            try {
                Response response = requestCall.execute();
                if (response.isSuccessful()) {
                    String json = response.body().string();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
    
        }
    

    1、获取OkHttpClient

    new OkHttpClient.Builder()
                    .connectTimeout(10000, TimeUnit.MILLISECONDS)
                    .readTimeout(10000, TimeUnit.MILLISECONDS)
                    .writeTimeout(10000, TimeUnit.MICROSECONDS)
                    .build()

    代表着okhttp请求的客户端类,很多的功能需要这个客户端类进行转发或者实现,而他的创建主要有两种方法:

    • 第一种方法
      • new OkHttpClient()
    • 第二种方法
      • new OkHttpClient.Builder().build()来创建
      • 因为要考虑要其他请求
        • 网络复杂(连接超时/读取超时…)
        • 需要设置其他参数(拦截器/分发器等…)

    2、获取Request请求对象

     Request request = new Request.Builder()
     .url("http://www.baidu.com")  //
                    .get()
                    .build();

    代表着请求的报文信息,比如请求的方法、请求头、请求地址,这个Request也是通过构建者模式来创建。

    3、获取okhttp3.Call对象

     Call requestCall = getOkHttpClient().newCall(request);

    这个Call对象,就相当于实际的OkHttp请求,也就是可以把他理解为是Request请求和Response响应的一个桥梁,通过client的newCall()方法,传入我们之前创建好的request对象。要注意,这个之前和同步请求和异步请求没什么区别,在接下来的步骤就是实现的同步或者异步请求的逻辑了。也就是前三步骤只是为了获取实际的请求Call。

    4、Call对象的execute()同步请求方法

      //同步请求
      Response response = requestCall.execute();
      //响应成功
      if (response.isSuccessful()) {
          //通过响应的body().string()方法获取返回回来的json数据(也可以是其他类型的数据(XML类型)  这个需要和服务器端商量好)
          String json = response.body().string();
      }

    通过execute()方法来获取响应报文的读取,Response顾名思义就是响应体,其中包含了响应头,响应实体信息,同步和异步请求最大的区别就是这个第四步骤,因为异步请求调用的则是enqueue()方法,传入CallBack进行数据的成功和失败逻辑

    同步请求总结

    • 1、创建OkHttpClient和Requet对象
    • 2、将Request封装成Call对象
    • 3、调用Call的execute()方法同步请求

    同步请求发送之后,就会进入阻塞状态,直到收到响应。而且也只有同步请求会阻塞,异步请求是新开的线程做网络请求.

    如下图:不管事同步还是异步请求,最终都会经过getResponseWithInterceptorChain()方法 这个方法 构造一个拦截器链,经过不同的拦截器,最后返回数据。(后面会介绍)

    image.png

    异步请求

    • 前三步骤和同步请求类似,不同的是第四个步骤也就是下方的代码逻辑,主要是call的enqueue()方法,传入相应的CallBack进行数据返回之后的返回和错误处理。
     requestCall.enqueue(new Callback() {
                @Override
                public void onFailure(Call call, IOException e) {
                    //当请求出错的时候  会回调到这个方法中
                }
    
                @Override
                public void onResponse(Call call, Response response) throws IOException {
                    //要注意这个地方是子线程,okhttp并没有给我们把响应的数据转换到主线程中,因此我们在这个地方更新UI的时候
                    //是会报错的  需要我们手动的转换到主线程  然后才进行数据的解析和UI更新   在之前的retrofit的讲解中
                    //也稍微有所涉及  那就是retrofit已经帮我们把异步的这个请求  返回来的callback已经切换到了主线程
                    //得益于retrofit里面的Executorer这个回调执行器
                    if (response.isSuccessful()){
                        String string = response.body().string();
                    }
                }
            });

    因此可以看到,同步请求和异步请求的差异在于第四步,也就是execute()同步方法和enqueue()异步方法。

    展开全文
  • ASI教学异步

    2013-01-14 17:02:06
    ASI教程,异步,block,同步,下载,session,验证,登陆
  • 网络教学系统是在网络环境下,充分发挥网络的教育功能和教育资源优势,向教育者和学习者提供的一种教和学的环境,通过传递数字化教育信息,开展交互式的同步异步教学活动。由于其具有教学资源共享、学习时空不限、...
  • 网络远程教学是随着计算机的普及和互联网的崛起而发展起来的一种新型教学方式,并随着技术和设备等各方面的成熟将成为...1 目前网络远程教学中存在的问题 网络远程教学同步异步两种教学模式。目前网络远程教学
  • * * 进程管理 进程同步 定义我们把在异步环境下一组并发进程因直接制约互相发送消息并进行互相合作互相等待使得各进程按一定速度执行过程称为进程间同步 具有同步关系一组并发进程称为合作进程合作进程间互相发送...
  • setState 到底是同步的,还是异步的?   setState 对于许多的 React 开发者来说,像是一个“最熟悉的陌生人”: 当你入门 React 的时候,接触的第一波 API 里一定有 setState——数据驱动视图,没它就没法创造...

    setState 到底是同步的,还是异步的?

      setState 对于许多的 React 开发者来说,像是一个“最熟悉的陌生人”:

    • 当你入门 React 的时候,接触的第一波 API 里一定有 setState——数据驱动视图,没它就没法创造变化;
    • 当你项目的数据流乱作一团的时候,层层排查到最后,始作俑者也往往是 setState——工作机制太复杂,文档又不说清楚,只能先“摸着石头过河”。

      setState 的工作机制渐渐与 React 调和算法并驾齐驱,成了 React 核心原理中区分度最高的知识模块之一。

    从一道面试题说起

      给出一个这样的 App 组件,在它的内部会有如下代码所示的几个不同的 setState 操作:

    import React, { Component } from 'react';
    
    export default class App extends Component {
      state = {
        count: 0
      };
    
      handleIncrement = () => {
        console.log('increment setState前的count', this.state.count);
        this.setState({
          count: this.state.count + 1
        });
        console.log('increment setState后的count', this.state.count);
      };
    
      handleTriple = () => {
        console.log('triple setState前的count', this.state.count);
        this.setState({
          count: this.state.count + 1
        });
        this.setState({
          count: this.state.count + 1
        });
        this.setState({
          count: this.state.count + 1
        });
        console.log('triple setState后的count', this.state.count);
      };
    
      handleReduce = () => {
        setTimeout(() => {
          console.log('reduce setState前的count', this.state.count);
          this.setState({
            count: this.state.count - 1
          });
          console.log('reduce setState后的count', this.state.count);
        }, 0);
      };
    
      render() {
        return (
          <div>
            <div>{this.state.count}</div>
            <button onClick={this.handleIncrement}>点我增加</button>
            <button onClick={this.handleTriple}>点我增加三倍</button>
            <button onClick={this.handleReduce}>点我减少</button>
          </div>
        );
      }
    }
    

      此时有个问题,若从左到右依次点击每个按钮,控制台的输出会是什么样的?读到这里,建议你先暂停 1 分钟在脑子里跑一下代码,看看和下图实际运行出来的结果是否有出入。

    在这里插入图片描述

      如果你是一个熟手 React 开发,那么 handleIncrement这个方法的输出结果想必难不倒你——正如许许多多的 React 入门教学所声称的那样,“setState是一个异步的方法”,这意味着当我们执行完 setState 后,state本身并不会立刻发生改变。 因此紧跟在 setState后面输出的 state值,仍然会维持在它的初始状态(0)。在同步代码执行完毕后的某个“神奇时刻”,state才会“恰恰好”地增加到 1。

      但这个“神奇时刻”到底何时发生,所谓的“恰恰好”又如何界定呢?如果你对这个问题搞不太清楚,那么 triple方法的输出对你来说就会有一定的迷惑性——setState一次不好使, setState三次也没用,state 到底是在哪个环节发生了变化呢?

      带着这样的困惑,你决定先抛开一切去看看 handleReduce方法里是什么光景,结果更令人大跌眼镜,reduce 方法里的 setState竟然是同步更新的!这…到底是我们初学 React时拿到了错误的基础教程,还是电脑坏了?

    异步的动机和原理——批量更新的艺术

      在setState 调用之后,都发生了哪些事情?基于截止到现在的专栏知识储备,可能会更倾向于站在生命周期的角度去思考这个问题,得出一个如下图所示的结论:

    在这里插入图片描述

      从图上我们可以看出,一个完整的更新流程,涉及了包括 re-render(重渲染) 在内的多个步骤。re-render本身涉及对 DOM 的操作,它会带来较大的性能开销。假如说“一次 setState就触发一个完整的更新流程”这个结论成立,那么每一次 setState的调用都会触发一次 re-render,我们的视图很可能没刷新几次就卡死了。这个过程如我们下面代码中的箭头流程图所示:

    this.setState({
      count: this.state.count + 1    ===>    shouldComponentUpdate->componentWillUpdate->render->componentDidUpdate
    });
    this.setState({
      count: this.state.count + 1    ===>    shouldComponentUpdate->componentWillUpdate->render->componentDidUpdate
    });
    this.setState({
      count: this.state.count + 1    ===>    shouldComponentUpdate->componentWillUpdate->render->componentDidUpdate
    });
    

      事实上,这正是 setState异步的一个重要的动机——避免频繁的 re-render

      在实际的 React 运行时中,setState异步的实现方式有点类似于 Vue$nextTick 和浏览器里的 Event-Loop每来一个 setState,就把它塞进一个队列里“攒起来”。等时机成熟,再把“攒起来”的 state 结果做合并,最后只针对最新的 state 值走一次更新流程。这个过程,叫作“批量更新”,批量更新的过程正如下面代码中的箭头流程图所示:

    this.setState({
      count: this.state.count + 1    ===>    入队,[count+1的任务]
    });
    this.setState({
      count: this.state.count + 1    ===>    入队,[count+1的任务,count+1的任务]
    });
    this.setState({
      count: this.state.count + 1    ===>    入队, [count+1的任务,count+1的任务, count+1的任务]
    });
                                              ↓
                                             合并 state,[count+1的任务]
                                              ↓
                                             执行 count+1的任务
    

      值得注意的是,只要我们的同步代码还在执行,“攒起来”这个动作就不会停止。(注:这里之所以多次 +1 最终只有一次生效,是因为在同一个方法中多次 setState的合并动作不是单纯地将更新累加。比如这里对于相同属性的设置,React 只会为其保留最后一次的更新)。因此就算我们在 React 中写了这样一个 100 次的 setState 循环:

    test = () => {
      console.log('循环100次 setState前的count', this.state.count);
    
      for (let i = 0; i < 100; i++) {
        this.setState({
          count: this.state.count + 1
        });
      }
    
      console.log('循环100次 setState后的count', this.state.count);
    };
    

      也只是会增加 state 任务入队的次数,并不会带来频繁的 re-render。当 100 次调用结束后,仅仅是 state 的任务队列内容发生了变化, state 本身并不会立刻改变:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D6DRdR0N-1636959262457)(https://s0.lgstatic.com/i/image/M00/6D/8B/Ciqc1F-uMfKALHLXAAEBeCrt5lE676.png)]

    循环100次 setState前的count 0
    App.js:17 循环100次 setState后的count 0
    

    “同步现象”背后的故事:从源码角度看 setState 工作流

      接下来要重点理解刚刚代码里最诡异的一部分——setState的同步现象:

      handleReduce = () => {
        setTimeout(() => {
          console.log('reduce setState前的count', this.state.count);
          this.setState({
            count: this.state.count - 1
          });
          console.log('reduce setState后的count', this.state.count);
        }, 0);
      };
    

      从题目上看,setState似乎是在 setTimeout函数的“保护”之下,才有了同步这一“特异功能”。事实也的确如此,假如我们把 setTimeout摘掉,setState前后的 console表现将会与 handleIncrement 方法中无异:

      handleReduce = () => {
        //setTimeout(() => {
          console.log('reduce setState前的count', this.state.count);
          this.setState({
            count: this.state.count - 1
          });
          console.log('reduce setState后的count', this.state.count);
        //}, 0);
      };
    
    执行结果:  
    reduce setState前的count 0
    App.js:36 reduce setState后的count 0
    

      现在问题就变得清晰多了:为什么 setTimeout 可以将 setState 的执行顺序从异步变为同步?

      并不是 setTimeout 改变了 setState,而是 setTimeout 帮助 setState “逃脱”了 React 对它的管控。只要是在 React 管控下的 setState,一定是异步的

    解读 setState 工作流

      React 中对于功能的拆分是比较细致的,setState这部分涉及了多个方法。为了方便理解,这里先把主流程提取为一张大图:

    在这里插入图片描述

      接下来我们就沿着这个流程,逐个在源码中对号入座。首先是 setState 入口函数:

    ReactComponent.prototype.setState = function (partialState, callback) {
      this.updater.enqueueSetState(this, partialState);
      if (callback) {
        this.updater.enqueueCallback(this, callback, 'setState');
      }
    };
    

      入口函数在这里就是充当一个分发器的角色,根据入参的不同,将其分发到不同的功能函数中去。这里我们以对象形式的入参为例,可以看到它直接调用了 this.updater.enqueueSetState这个方法:

    enqueueSetState: function (publicInstance, partialState) {
      // 根据 this 拿到对应的组件实例
      var internalInstance = getInternalInstanceReadyForUpdate(publicInstance, 'setState');
      // 这个 queue 对应的就是一个组件实例的 state 数组
      var queue = internalInstance._pendingStateQueue || (internalInstance._pendingStateQueue = []);
      queue.push(partialState);
      //  enqueueUpdate 用来处理当前的组件实例
      enqueueUpdate(internalInstance);
    }
    

    这里总结一下,enqueueSetState做了两件事:

    • 将新的 state 放进组件的状态队列里;
    • 用 enqueueUpdate 来处理将要更新的实例对象。

      继续往下走,看看 enqueueUpdate 做了什么:

    function enqueueUpdate(component) {
      ensureInjected();
      // 注意这一句是问题的关键,isBatchingUpdates标识着当前是否处于批量创建/更新组件的阶段
      if (!batchingStrategy.isBatchingUpdates) {
        // 若当前没有处于批量创建/更新组件的阶段,则立即更新组件
        batchingStrategy.batchedUpdates(enqueueUpdate, component);
        return;
      }
      // 否则,先把组件塞入 dirtyComponents 队列里,让它“再等等”
      dirtyComponents.push(component);
      if (component._updateBatchNumber == null) {
        component._updateBatchNumber = updateBatchNumber + 1;
      }
    }
    

      这个 enqueueUpdate非常有嚼头,它引出了一个关键的对象——batchingStrategy,该对象所具备的isBatchingUpdates属性直接决定了当下是要走更新流程,还是应该排队等待;其中的batchedUpdates方法更是能够直接发起更新流程。由此可以大胆推测,batchingStrategy 或许正是 React 内部专门用于管控批量更新的对象

      接下来,我们就一起来研究研究这个 batchingStrategy

    /**
     * batchingStrategy源码
    **/
    var ReactDefaultBatchingStrategy = {
      // 全局唯一的锁标识
      isBatchingUpdates: false,
      // 发起更新动作的方法
      batchedUpdates: function(callback, a, b, c, d, e) {
        // 缓存锁变量
        var alreadyBatchingStrategy = ReactDefaultBatchingStrategy. isBatchingUpdates
        // 把锁“锁上”
        ReactDefaultBatchingStrategy. isBatchingUpdates = true
        if (alreadyBatchingStrategy) {
          callback(a, b, c, d, e)
        } else {
          // 启动事务,将 callback 放进事务里执行
          transaction.perform(callback, null, a, b, c, d, e)
        }
      }
    }
    

      batchingStrategy对象并不复杂,你可以理解为它是一个“锁管理器”。

      这里的“锁”,是指 React 全局唯一的 isBatchingUpdates变量,isBatchingUpdates的初始值是 false,意味着“当前并未进行任何批量更新操作”。每当 React 调用 batchedUpdate去执行更新动作时,会先把这个锁给“锁上”(置为 true),表明“现在正处于批量更新过程中”。当锁被“锁上”的时候,任何需要更新的组件都只能暂时进入 dirtyComponents里排队等候下一次的批量更新,而不能随意“插队”。此处体现的“任务锁”的思想,是 React 面对大量状态仍然能够实现有序分批处理的基石。

      理解了批量更新整体的管理机制,还需要注意 batchedUpdates中,有一个引人注目的调用:

    transaction.perform(callback, null, a, b, c, d, e)
    

      这行代码为我们引出了一个更为硬核的概念——React 中的 Transaction(事务)机制。

    理解 React 中的 Transaction(事务) 机制

      Transaction 在 React 源码中的分布可以说非常广泛。如果在 Debug React 项目的过程中,发现函数调用栈中出现了 initializeperformclosecloseAll或者 notifyAll这样的方法名,那么很可能当前就处于一个 Trasaction中。

      Transaction 在 React 源码中表现为一个核心类,React 官方曾经这样描述它:Transaction 是创建一个黑盒,该黑盒能够封装任何的方法。因此,那些需要在函数运行前、后运行的方法可以通过此方法封装(即使函数运行中有异常抛出,这些固定的方法仍可运行),实例化 Transaction 时只需提供相关的方法即可。

      这段话初读有点拗口,结合 React 源码中的一段针对 Transaction 的注释来理解它

    * <pre>
     *                       wrappers (injected at creation time)
     *                                      +        +
     *                                      |        |
     *                    +-----------------|--------|--------------+
     *                    |                 v        |              |
     *                    |      +---------------+   |              |
     *                    |   +--|    wrapper1   |---|----+         |
     *                    |   |  +---------------+   v    |         |
     *                    |   |          +-------------+  |         |
     *                    |   |     +----|   wrapper2  |--------+   |
     *                    |   |     |    +-------------+  |     |   |
     *                    |   |     |                     |     |   |
     *                    |   v     v                     v     v   | wrapper
     *                    | +---+ +---+   +---------+   +---+ +---+ | invariants
     * perform(anyMethod) | |   | |   |   |         |   |   | |   | | maintained
     * +----------------->|-|---|-|---|-->|anyMethod|---|---|-|---|-|-------->
     *                    | |   | |   |   |         |   |   | |   | |
     *                    | |   | |   |   |         |   |   | |   | |
     *                    | |   | |   |   |         |   |   | |   | |
     *                    | +---+ +---+   +---------+   +---+ +---+ |
     *                    |  initialize                    close    |
     *                    +-----------------------------------------+
     * </pre>
    

      说白了,Transaction就像是一个“壳子”,它首先会将目标函数用 wrapper(一组 initializeclose方法称为一个 wrapper) 封装起来,同时需要使用 Transaction 类暴露的 perform方法去执行它。如上面的注释所示,在 anyMethod执行之前,perform会先执行所有 wrapperinitialize方法,执行完后,再执行所有 wrapperclose方法。这就是 React 中的事务机制。

    “同步现象”的本质

      下面结合对事务机制的理解,继续来看在 ReactDefaultBatchingStrategy这个对象。ReactDefaultBatchingStrategy其实就是一个批量更新策略事务,它的 wrapper有两个:FLUSH_BATCHED_UPDATESRESET_BATCHED_UPDATES

    var RESET_BATCHED_UPDATES = {
      initialize: emptyFunction,
      close: function () {
        ReactDefaultBatchingStrategy.isBatchingUpdates = false;
      }
    };
    var FLUSH_BATCHED_UPDATES = {
      initialize: emptyFunction,
      close: ReactUpdates.flushBatchedUpdates.bind(ReactUpdates)
    };
    var TRANSACTION_WRAPPERS = [FLUSH_BATCHED_UPDATES, RESET_BATCHED_UPDATES];
    

      把这两个 wrapper套进 Transaction的执行机制里,不难得出一个这样的流程:

    callback执行完成后,RESET_BATCHED_UPDATESisBatchingUpdates置为falseFLUSH_BATCHED_UPDATES执行flusfBatchedUpdates,然后里面会循环所有dirtyComponent,调用updateComponent来执行所有的生命周期方法(componentWillReceiveProps=>shouldComponentUpdate=>componentWillUpdate=>render=>componentDidUpdate),最后实现组件更新。

      到这里,相信已经对 isBatchingUpdates管控下的批量更新机制已经了然于胸。但是 setState为何会表现同步这个问题,似乎还是没有从当前展示出来的源码里得到根本上的回答。这是因为 batchedUpdates这个方法,不仅仅会在 setState之后才被调用。若我们在 React 源码中全局搜索 batchedUpdates,会发现调用它的地方很多,但与更新流有关的只有这两个地方:

    // ReactMount.js
    _renderNewRootComponent: function( nextElement, container, shouldReuseMarkup, context ) {
      // 实例化组件
      var componentInstance = instantiateReactComponent(nextElement);
      // 初始渲染直接调用 batchedUpdates 进行同步渲染
      ReactUpdates.batchedUpdates(
        batchedMountComponentIntoNode,
        componentInstance,
        container,
        shouldReuseMarkup,
        context
      );
      ...
    }
    

      这段代码是在首次渲染组件时会执行的一个方法,我们看到它内部调用了一次 batchedUpdates,这是因为在组件的渲染过程中,会按照顺序调用各个生命周期函数。开发者很有可能在声明周期函数中调用 setState。因此,我们需要通过开启 batch来确保所有的更新都能够进入 dirtyComponents里去,进而确保初始渲染流程中所有的 setState都是生效的。

      下面代码是 React 事件系统的一部分。当我们在组件上绑定了事件之后,事件中也有可能会触发 setState。为了确保每一次 setState都有效,React同样会在此处手动开启批量更新。

    // ReactEventListener.js
    dispatchEvent: function (topLevelType, nativeEvent) {
      ...
      try {
        // 处理事件
        ReactUpdates.batchedUpdates(handleTopLevelImpl, bookKeeping);
      } finally {
        TopLevelCallbackBookKeeping.release(bookKeeping);
      }
    }
    

      话说到这里,一切都变得明朗了起来:isBatchingUpdates这个变量,在 React 的生命周期函数以及合成事件执行前,已经被 React 悄悄修改为了 true,这时我们所做的 setState操作自然不会立即生效。当函数执行完毕后,事务的 close方法会再把 isBatchingUpdates改为 false

      以开头示例中的 increment 方法为例,整个过程像是这样:

      handleIncrement = () => {
        // 进来先锁上
        isBatchingUpdates = true;
        console.log('increment setState前的count', this.state.count);
        this.setState({
          count: this.state.count + 1
        });
        console.log('increment setState后的count', this.state.count);
        // 执行完函数再放开
        isBatchingUpdates = false;
      };
    

      很明显,在 isBatchingUpdates的约束下,setState只能是异步的。而当 setTimeout从中作祟时,事情就会发生一点点变化:

      handleReduce = () => {
        // 进来先锁上
        isBatchingUpdates = true;
        setTimeout(() => {
          console.log('reduce setState前的count', this.state.count);
          this.setState({
            count: this.state.count - 1
          });
          console.log('reduce setState后的count', this.state.count);
        }, 0);
        // 执行完函数再放开
        isBatchingUpdates = false;
      };
    

      会发现,咱们开头锁上的那个 isBatchingUpdates,对 setTimeout内部的执行逻辑完全没有约束力。因为 isBatchingUpdates是在同步代码中变化的,而 setTimeout的逻辑是异步执行的。当 this.setState调用真正发生的时候,isBatchingUpdates早已经被重置为了 false,这就使得当前场景下的 setState 具备了立刻发起同步更新的能力。所以咱们前面说的没错——setState 并不是具备同步这种特性,只是在特定的情境下,它会从 React 的异步管控中“逃脱”掉

    总结

      对整个 setState 工作流做一个总结:

    setState并不是单纯同步/异步的,它的表现会因调用场景的不同而不同:在 React 钩子函数及合成事件中,它表现为异步;而在 setTimeoutsetInterval等函数中,包括在 DOM 原生事件中,它都表现为同步。这种差异,本质上是由 React 事务机制和批量更新机制的工作方式来决定的。

    展开全文
  • 因此,本文将异步同步游戏玩法视为向学生教授词汇的实用工具。 本文涉及使用集成的异步同步游戏玩法向年轻学习者教授英语词汇的文献综述。 本文还将讨论在词汇教学中使用游戏玩法的重要性,为什么将游戏玩法...
  • 最近更新了一系列关于异步和回调的文章,比如《一篇文章,搞明白异步和多线程的区别》、《两个经典例子让你彻底理解java回调机制...同时,我们也知道了“服务实现的异步同步特性完全独立于客户端调用的异步同步特性
  • Minix 是一个迷你版本的类 Unix 操作系统,由塔能鲍姆教授为了教学之用而创作,采用微核心设计。它启发了 Linux 内核的创作。 1991 年,Linus Torvalds 开源了 Linux 内核。Linux 以一只可爱的企鹅作为标志,象征着...
  • http://www.maiziedu.com/course/u3d/586-8498/
  • Ajax中的同步异步

    2020-02-28 16:41:56
    AJAX的异步 let xhr = new XMLHttpRequest ; xhr . open ( 'GET' , 'json/data.json' ) ; //=>设置事件绑定之前状态1 xhr . onreadystatechange = function ( ) { console . log ( xhr . ...
  • 在爬虫中使用异步实现高性能的数据爬取操作。 2、实现方式 (1)多线程 / 多进程(不建议):好处:可以为相关阻塞的操作单独开启线程或者进程,阻塞操作就可以异步执行。弊端:无法无限制的开启多线程或者多进程...
  • 计算机网络基础教学

    2021-06-25 01:55:51
    计算机网络基础教学4.5公共电话网即PSTN(Public Swithed TelephoneNetwork),速度9600bps~28.8kbps,经压缩后最高可达115.2kbps,传输介质是普通电话线。4.6综合业务数字网即ISDN(Integrated Service ...
  • 事件对象 时间对象也属于内核对象,包含一个使用计数,一个用于指明该事件是一个自动重置的事件还是一个人工重置的...这段代码在教学视频上在文本框中输入的是“sunxin”,我认为那是那台电脑的在网络上的主机名,而在...
  • 1.事件对象:来实现线程的同步。与互斥对象一样均属于内核对象。 当人工重置有信号时,所有线程均得到信号,所以不能设为人工重置。代码就不贴了。 通过创建匿名的事件对象,也可以让一个程序只能运行一个实例。 ...
  • 《分布式系统》教学大纲

    千次阅读 2020-03-12 08:41:01
    《分布式系统》教学大纲 课程编导:081011303 081017303 081212407 081217391 课程名称:编译原理 学分:2 总学时:32 课内上机学时:4 先修课程要求:计算机网络原理、操作系统、软件技术 适应专业:软件工程...
  • (1)异步调制 异步调制——载波信号和调制信号不同步的调制方式。 通常保持 fc 固定不变,当 fr 变化时,载波比 N 是变化的; 在信号波的半周期内,PWM波的脉冲个数不固定,相位也不固定,正负半周期的脉冲不对称...
  • 1计算机网络教学的特点1.1校园数字化,教材科学化数字化校园的建设随着计算机网络化教学的开展而不断的完善,在国内外的教育领域受到了广泛的关注和认可,学校的各个方面的建设都可以实现数字化,包括校园环境、资源...
  • 《计算机应用基础中分层次教学与分组指导的实践研究(原稿).doc》由会员分享,可免费在线阅读全文,更多与《计算机应用基础中分层次教学与分组指导的实践研究(原稿)》相关文档资源请在帮帮文库(www.woc88.com)数亿...
  • 谢业斌摘 要:ThinkPHP属于一种计算机领域的开发解决方案,具有一定的系统...为了体现ThinkPHP平台教师教学计划结合的优势,确保辅助教师教学,同时促進学生学习兴趣与效率提升,本文探讨了应用ThinkPHP框架设计教...
  • 一、网络教学模式下的计算机教育的教学模式(一)讲授型模式讲授型模式其实是传统授课方式在网络基础上的新发展,主要是以教师讲授学习内容为主,并利用网络将教师和学生联系起来,系统授课,从而达到教学目的。...
  • 困扰了好几日的问题,其实并不是所有的rman备份都会被推入到SGA区的,要分情况看,看系统是否支持异步I/O,还有是否配置了相关的从属I/O参数,磁盘和磁带需要分别看待。现在的书就算买理论的都讲得不清不楚,中国...
  • 所谓网络教学,是指教师利用计算机网络为手段对学生传递教学信息,学生通过计算机网络获取知识。下面是学习啦小编给大家推荐的计算机网络教学探讨论文,希望大家喜欢!计算机网络教学探讨论文篇一《浅析计算机网络...
  • 什么是异步?为什么要用它?异步编程提供了一个非阻塞的,事件驱动的编程模型。这种编程模型利用系统中多核执行任务来提供并行,因此提供了应用的吞吐率。此处吞吐率是指在单位时间内所做任务的数量。在这种编程方式...
  • UML网络教学系统

    2015-04-26 21:02:57
    网络教学系统是在网络环境下,充分发挥网络的教育功能和教育资源优势,向教育者和学习者提供的一种教和学的环境,通过传递数字化教育信息,开展交互式的同步异步教学活动。由于其具有教学资源共享、学习时空不限、...
  •  如果你是一个熟手 React 开发,那么 increment 这个方法的输出结果想必难不倒你——正如许许多多的 React 入门教学所声称的那样,“setState 是一个异步的方法”,这意味着当我们执行完 setState 后,state 本身并...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 5,256
精华内容 2,102
关键字:

同步教学异步教学