两行代码引发的血案-代笔司徒老师

最近一个体量很小的 JavaScript 库is-promise(npm地址:https://www.npmjs.com/package/p-is-promise,Githubf地址:https://github.com/sindresorhus/p-is-promise)进行了更新,由于最新版本没有遵循正确的 ES 模块标准,使得超过300万个引用了is-promise的前端项目均出现了问题,这个问题甚至让整个 JavaScript 生态系统陷入了混乱。由于前端项目的构造方式与中后台项目的机制不同,这种由小型项目引起广泛问题的情况已经不是第一次发生了。

前两天小扎的FaceBook也因为SDK中的两行代码使很多IOS的应用全部陷入了崩溃的境地。

 

而当笔者看到这样的新闻,心中不由得一颤,因为前端恰恰司徒正美老师生前一直奋斗的领域,而我来写这篇文章,执笔当哭,缅怀旧人。

//创建一个Promise对象,定义resolve方法,在3000ms后执行。

const promise1 = new Promise(function(resolve, reject) {

  setTimeout(function() {

    resolve('promise');

  }, 3000);

});

//非阻塞执行promise1,在promise完成后此方法会被执行。

promise1.then(function(value) {

  console.log(value);

  // 3s后会输出promise

});



console.log(promise1);

//直接输出 [object Promise]

笔者和司徒结识于CSDN,记得是去年6月,当时我看到一篇博文《前端开发 20 年变迁史》,心中不由赞叹,竟有人能将前端技术上升到哲学高度来进行阐释,于是千方百信找到了司徒的微信加为好友,和司徒聊天中,明显能感受到他对于前端技术的理解深度和积极热情。谁知天有不测风云,司徒年纪轻轻竟然溘然长逝,在此笔者也提示各位读者朋友们保重身份,切莫透支健康。下面笔者就尝试代替司徒,帮助大家解读一下这则新闻背后的技术细节。由于前端并不是笔者的领域,如有错漏还请各位指正。

初识promise

因为笔者对于JavaScript也不是特别了解,而初步学习了之后我看到了阻塞代码,非阻塞代码,事件驱动设计模式,事件生命周期,函数堆栈,事件队列,等等概念,以及polyfill,babel,angular,reactJS,vueJS 等众多框架。但是归根结底JavaScript是一种同步编程语言。

但是由于具备非阻塞和回调机制,JavaScript也可以实现异步功能。而根据MDN上对于Promise机制的描述(https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise),Promise对象是一个代理对象(代理一个值),被代理的值在Promise对象创建时可能是未知的。它允许你为异步操作的成功和失败分别绑定相应的处理方法(handlers)。 这让异步方法可以像同步方法那样返回值,但并不是立即返回最终执行结果,而是一个能代表未来出现的结果的promise对象。示例及注释如下:

 

综上笔者认为可以将Promise理解成Java中的Callback,用以帮助JavaScript实现异步功能。

而本次出现问题的is-promise包,就是用来测试一个JavaScript对象是否为Promise的。is-promise包的代码其实非常短,其主要的功能实现代码只有两行而已。前端开发人员在引入is-promise包之后,就能在自己的项目中引用它,而且is-promise包是基于MIT协议的,因此引用该项目,也不必须要求开源。虽然只有两行代码,但is-promise 库却是当今最受欢迎的 JavaScript 软件包之一。据不完全核计is-promise是700余个知名的JavaScript 库的依赖项,其影响项目数量至少超过300万,范围涵盖至封闭源 JavaScript 代码库和 JavaScript 生态系统中一些最大的项目。其中包括有:Facebook 的 Create React App(用于创建 React 应用程序的标准模板)、谷歌的 Angular.js 框架、谷歌的 Firebasse-tools、亚马逊的 AWS Serverless CLI、Nuxt.js 和 AVA 等。

引发问题的ES 模块标准

 is-promise 库之所以引发问题,关键在于他没有遵循正确的ES模块标准,而提到ES模块标准,我们还要从最基本的概念聊起。我们知道JavaScript是一门动态的脚本化的语言,它使得前端页面的开发变得非常简单。

JavaScript的编程范式抽象成维护变量,赋值和计算操作。大量的代码在用于操作变量,开发者需要懂得如何去组织和维护这些变量。JavaScript 提供了一种方式,即函数作用域。在一个函数内只需要考虑这个函数的变量问题。不必去担心其他函数会操作这些变量。当然,随之带来的问题是,变量无法共享,无法在不同的函数之间相互共享变量。如  果想要在作用域外共享变量,只能通过外层作用域,或者全局作用域。

而ES模块标准,则提供了更好的方式来组织变量和函数,把相关的变量和函数组织到一起。具体就是将这些函数和变量放到一个模块作用域内,实现在模块间共享变量。与函数作用域不同的是,模块内部的变量实现了在其他模块内共享。而且可以指定哪些变量、类或者函数可以共享。在其他模块中共享,被称为 export。这就出现了模块间的依赖,是一种很明确的关系,当移除一个模块时可以准确的知道哪些模块会出错。一旦有了模块间导出和引用变量的能力,我们就可以将代码打成小包。然后就可以像乐高玩具那样组合,再组合。使用小模块就可以创建出各类应用。在使用模块的时候,其实就是在做一个依赖关系图。ESM的模块包括三个过程:

1:构建:下载,解析,然后把文件解析为模块记录

2:实例化:为模块分内存空间(此时还没赋值),然后依照导入,导出语句把模块指向内存地址,这个过程叫链接

3:运行(求值):运行代码的时候,才会给内存空间填充真实的值

而ES则通过一系列的标准来确保相关代码可以实现上述模块化的功能。

is-promise v.2.2.0 版本却未遵循正确的 ES 模块标准。因此在其更新发布后,引用了is-promise在各个项目都在的构建链时出现了问题。

总结一下本文内容Promise是Javascript中实现异步功能的重要机制,is-promise又是目前流行度最高的promise对象检测工具,而由于is-promise没有遵循正确的ES模块标准,使得其它引用了promise包的程序出现了问题。

最后笔者还是引用司徒正美老师生前名言做结。当初JavaScript被误解为最糟糕的语言,时至今日它是最流行的语言,GitHub 60%的项目都是与JavaScript有关。任何可以使用JavaScript来编写的应用,最终会由JavaScript编写。愿前端开发之路越来越好!

猜你喜欢

转载自blog.csdn.net/BEYONDMA/article/details/106157396