一把锁引发的思考

大概半个多月前就有用户反馈:同时打开文件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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,907评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,987评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,298评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,586评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,633评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,488评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,275评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,176评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,619评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,819评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,932评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,655评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,265评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,871评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,994评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,095评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,884评论 2 354

推荐阅读更多精彩内容

  • 上一章介绍了模块的语法,本章介绍如何在浏览器和 Node 之中加载 ES6 模块,以及实际开发中经常遇到的一些问题...
    emmet7life阅读 2,752评论 0 1
  • 前言 java有类文件,Python有import机制,Ruby有require等,而Javascript 通过 ...
    RayLeo阅读 795评论 0 4
  • 本文为阮一峰大神的《ECMAScript 6 入门》的个人版提纯! babel babel负责将JS高级语法转义,...
    Devildi已被占用阅读 1,985评论 0 4
  • 模块通常是指编程语言所提供的代码组织机制,利用此机制可将程序拆解为独立且通用的代码单元。所谓模块化主要是解决代码分...
    MapleLeafFall阅读 1,170评论 0 0
  • 嗨!读书吧的天使们,现在好!我是你们的朋友非凡梦,网名非凡梦,真实名字崔孟立,来自广西省河池市凤山地质公园,是个人...
    非凡梦阅读 1,045评论 0 0