美文网首页
一把锁引发的思考

一把锁引发的思考

作者: 小丸子啦啦啦呀 | 来源:发表于2018-05-05 19:08 被阅读0次

大概半个多月前就有用户反馈:同时打开文件A和文件B,在A的某个单元格中写几行执行起来非常耗时的代码,在等待A执行完成的时候,我再切到文件B,去执行B中的代码,发现无法执行,刷新整个页面之后才OK。
听到这个反馈,我第一反应是,有可能是执行某些耗时长,占用资源大的代码时,内核挂掉了,于是我立马重启一下内核,发现内核能够重启成功,重启之后照样不能执行代码。
排掉这个因素,我想,是不是socket受不住断掉了,于是试了一下代码提示功能,发现代码提示功能正常,说明socket连接也没问题。
在看socket的时候我顺便看了一下Frames,点击执行按钮的时候,事件并没有触发,那这意味着run方法没有执行:

// 触发socket 执行代码
  run(focusLine) {
    this.setState({ runLine: focusLine, kernelStatus: 'busy' });
    const { byId, socket } = this.props;
    console.log('待执行代码:', byId[focusLine].source.join('\n'), focusLine);
   // 如果这一句执行了,那么肯定可以看到对应Frame
    socket.emit('codeExe', byId[focusLine].source.join('\n'), focusLine);
  }

run方法其实并不是点击按钮时的回调,单步执行的回调是:

// 单步运行
  runStep = needGoNext => {
    this.needGoNext = needGoNext;
    this.msgObj = {};
    const {  focusLine } = this.props;
    console.log('runstep', finishFlag);
    if (!this.finishFlag) return;
    this.finishFlag = false;
    this.run(focusLine);
  };

那难道是runStep没有执行?可是console.log('runstep', finishFlag);能看到打印结果,说明点击事件绝对是生效了,那么就只有一个可能了:finishFlag这把锁造成的。

这把锁干什么用的?

finishFlag就是为了保证上一次执行完成才能触发下一次的执行。
如果不加锁,会造成代码执行结果的重复输出。
这把锁的逻辑很简单:

// 初始化锁为已完成状态
let finishFlag = true;
// notebook组件
export default class Notebook extends Component {
     // 单步运行
    runStep = needGoNext => {
      this.needGoNext = needGoNext;
      this.msgObj = {};
      const {  focusLine } = this.props;
      console.log('runstep', finishFlag);
      // 上一次执行未完成,直接返回
      if (!this.finishFlag) return;
      // 上锁
      this.finishFlag = false;
      this.run(focusLine);
      // 不在这儿解锁,解锁的时机是收到idel片段时
    };
     return (<div>...notebook...<div>)
}
// 父组件
import Notebook from '../...'
export default class EditorTabs extends Component {
   return
( <div>
      <Notebook />
      <Notebook />
</div>)
}

再想想错误现象,一个notebook无法执行的时候,另一个也没法执行了,那说明两个Notebook组件共享了这一个finishFlag,也就是说所有的Notebook都是是用的同一把锁!
为什么会这样呢?会不会有别的原因??
来做个实验好了,用create-react-app建一个空的项目,然后自己写一个这样的测试组件:

import React, { Component } from 'react';

let flag = true;
class Test extends Component {
    changeFlag = () => {
        console.log('----before', flag);
        flag = !flag;
        console.log('----after', flag);
    }
    render() {
        return <button onClick={this.changeFlag}>Test</button>
    }
}

export default Test;
import React, { Component } from 'react';
import Test from './Test';
class App extennds Component{
   return (
      <div>
          <Test />
          <Test />
          <Test />
          <Test /> 
      </div>
    );
}

如果每次点击输出都为

----before true
----after false

那说明是flag独立不受影响的。
但实验结果为:


image.png

再次验证了我的想法。
接下来把flag从组件外挪到组件内:

class Test extends Component {
    constructor(props) {
        super(props);
        this.flag = true;
    }
    changeFlag = () => {
        console.log('----before', flag);
        this.flag = !this.flag;
        console.log('----after', flag);
    }
    render() {
        return <button onClick={this.changeFlag}>Test</button>
    }
}

这样就没毛病了。
照葫芦画瓢我把finishFlag放到组件里,经过测试没有再出现用户反应的问题。

疑问

到这里,问题虽然解决了,但是我还是有些疑惑,这个flag到底在哪个作用域里?为什么没有被组件单独拥有呢?
看看Test模块的输出输入,然后在网上看了一些js模块化开发的博客,我终于懂了:

其实,flag属于这个Test.js模块,但是不属于这个App组件

export default Test;
import Test from '../..'

引申

  1. js模块化开发
    js不像java有package把作用域隔开,但是开发中我们又经常需要分隔作用域,人为制造模块。
    最古老的方式有:用function制造独立作用域,用对象集中管理不同作用域,立即执行函数制造独立作用域等;这样写起来很麻烦,需要开发人员去跟踪管理所有依赖关系;
    随后出现了CommonJS, 个人人为CommonJS最大的改进是:规定一个单独的文件就是一个模块,每一个模块都是一个单独的作用域。CommonJS使用require和module.exports函数来导入导出模块,但是使用require获取依赖模块的时候,由于同步的原因产生阻塞, 这样的缺点显而易见。
    随后出现了AMD和CMD,AMD使用requireJS,主要解决异步加载和前置引用的问题,CMD使用seaJs, 可以用到的时候再去require。但说实话这个时候JS都还没有原声支持“模块”,都要借助第三方库去做,直到ES6出来。
    从ES6出来后,有了新的模块化开发方式,即ES6模块化。也就是我现在用得最多的这种。ES6模块化其实很多引擎还不支持,需要用babel转成CommonJS规范的那种。那ES6模块化和CommonJS有什么区别呢?
    1.CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
    2.CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。

总结

以后不要轻易在组件外写变量!写之前想清楚能不能这样写。

参考

[前端模块化]http://www.cnblogs.com/dolphinX/p/4381855.html
[Javascript模块化编程(一):模块的写法
]http://www.ruanyifeng.com/blog/2012/10/javascript_module.html
[js模块化进程]https://blog.csdn.net/u014168594/article/details/77099315
[Module 的加载实现]http://es6.ruanyifeng.com/#docs/module-loader
[深入 JSX]http://www.css88.com/react/docs/jsx-in-depth.html

相关文章

  • 一把锁引发的思考

    大概半个多月前就有用户反馈:同时打开文件A和文件B,在A的某个单元格中写几行执行起来非常耗时的代码,在等待A执行完...

  • 一把钥匙引发的思考

    生活总是让人措手不及,学会微笑面对才是真正的人生赢家! ——题记 本来以为在这阴雨绵绵的天气可以美美的睡一...

  • 一把剪刀引发的思考

    昨天我们上午还在帮同事做百家衣,下午出去培训,听讲座。回啦同事说他工作的大剪刀不见了。我汗颜,那么大的工具谁要啊?...

  • 一把剪刀引发的思考

    前阵子在淘宝买了一把剪刀,等到货之后才发现自己买错了。 我原本是想买一把可以放在办公室桌上使用的文具型剪刀,长度在...

  • 二(1)魏梓恒作品

    1. 我是一把怎样的锁 我是一把锁,一把桃子锁,我是用桃子做成的,很甜很甜。 我是一把锁,一把牛...

  • 一把绿植引发的思考

    几株百合,一把文竹,还有宝贝养的三条小泥鳅,一起通通放入一个花瓶里!文竹养眼,百合散发着淡淡的香味,不仅养心还醒脑...

  • 一把钥匙🔑不可能开一百把锁

    一把钥匙不可能开一百把锁 一把钥匙不可能开一百把锁,所以我应该学会多种思维方式,站在不同的角度去思考问题。今天晚上...

  • "变"与"不变","辨"与"不辩"

    "从前的锁也好看,钥匙精美有样子,你锁了,人家就懂了。"一把锁,阖上一段过往;一把锁,封存一个时代;仍是一把锁,却...

  • 由“随薪锁欲”引发的思考

    在公众号看到一篇书单文,是由“随薪锁欲”一词开头的。 微博热词“随薪锁欲”,指当代年轻人原本有许多想做的事情,但奈...

  • 开锁

    一把钥匙开一把锁,一把锁配好几把钥匙,锁将军一夫当关,万夫莫开。 如今的锁五花八门,指纹锁、密码锁、声控锁、人脸识...

网友评论

      本文标题:一把锁引发的思考

      本文链接:https://www.haomeiwen.com/subject/zererftx.html