注意,由于本文讨论的是使用 TDD 方式实现一个 Promise,所以本文着重描述的是搭建 TDD 环境
Promise 能解决什么问题
先来看一段代码
fs.readdir(source, function (err, files) { |
可以看到着里面产生了几个回调,也就是那个著名的 callback hell
(回调地狱)
所以这有可能真的是回调地狱的问题么?来看看改善之后的代码吧
fs.readdir(source, (err, files) => { |
上面的代码没有回调么?当然有,那么为什么看起来清晰了呢?是因为这里将 函数当作参数传给要调用的函数了,所以其实对于 Promise
而言也是一样的
Promise 的优点
优点有俩
减少缩进(在整体代码的意义上)
将 函数套函数 的形式转变成 链式调用
f1(xxx, function f2(a) { |
转变成
f1(xxx) |
消灭 if(err) 形式的代码
- 关于错误的处理可以单独放到一个函数里面
- 如果不处理,则一直等到向后抛
f1(xxx) |
如何使用 Promise
对于一个异步的代码,以前是这么写
function test(fn) { |
如果使用 Promise
function test() { |
Promise 有哪些 API
- Promise 是一个类(相当于一个特殊的函数)
- 类属性:
length
- 类方法:
all
,allSettled
,race
,reject
,resolve
- 对象属性:
then
,finally
,catch
- 对象内部属性:
state
(pending, fullfilled, rejected)
这里注意,state
的 pending
状态的转变是单向的,即只有
- pending -> fullfilled(成功)
- pending -> rejected(失败)
并且 fullfilled 以及 rejected 状态不能互相转变,也不能转化成 pending
Promise API 怎么写
- 参考 promise/A+ 规范 或者其 翻译版文档
- 在写代码时根据文档写测试用例,规则都通过了说明
Promise
相关的逻辑也就完成了
使用测试工具
使用 chai
先全局安装两个包
yarn add -g ts-node mocha |
然后创建项目 promise-easy
,再执行
cd promise-easy |
最后再次给项目安装包
yarn add -D ts-node mocha chai @types/chai @types/mocha typescript |
然后创建 test/index.ts
touch test/index.ts |
最后增加 package.json
的 scripts
"scripts": { |
然后执行
yarn test |
出现如下打印输出则算配置成功
yarn run v1.21.1 |
然后说下这几个包的作用
chai
: chai 是一个 BDD/TDD 的断言库,我们经常使用其assert
api 来做测试种断言的操作mocha
: mocha 是一个多功能的测试框架,我们经常使用其describe
以及it
api 来做相关的测试ts-node
,typescript
: ts-node 是用来编译 node 中的 typescript 用的,使用 typescript 时需要安装 typescript@types/chai
,@types/mocha
: chai 和 mocha 的 typescript 版本,方便在 ts 文件中引入
现在就可以在 test/index.ts
中写代码了
import { assert } from 'chai' |
其中 describe
表示描述测试的场景,而 it
表示在该测试场景下的对象
注意,若在断言的那段代码的上面加
//@ts-ignore
即可屏蔽掉 typescript 对这段代码的检查,那么代码在编译运行时就不会报错
对于 assert
这个 api 来说,它里面有很多的函数比如经常使用到的有如下几个
isXXX
: 判断对象或变量的类型用的,比如isFunction
,isObject
throw
: 这个一般用于你想让其传入的回调函数中的代码报错
使用 sinon
安装 sinon
yarn add -D sinon sinon-chai @types/sinon @types/sinon-chai |
然后说下这几个包都是啥
sinon
: 适用于任何单元测试框架的测试库sinon-chai
: 为sinon
提供了一系列自自定义的断言 api,相当于 chai 的拓展@types/sinon
,@types/sinon-chai
: sinon 和 sinon-chai 的 typescript 版本
在这里主要是使用 sinon.fake()
提供一个假函数,然后通过判断这个假函数的 called
属性来判断函数是否被调用
你需要这样引入
import * as chai from 'chai' |
然后像这样使用
it('new Promise(fn) 中的函数立即执行', () => { |
总结
本文主要介绍了如何搭建测试环境,以及如何使用测试框架来进行开发(TDD)。其中比较重要的还设计到了 Promise
的几个要点,即
- Promise 要解决什么问题 - why
- Promise 是怎么解决它的 - how
- Promise (对比其他技术)有什么有点 - pros
- Promise有什么缺点 - cons
- 如何解决这些缺点
框架和环境已经搭建完毕,接下来就是遵循 Promise/A+ 规范实现一个简易 Promise