前置知识:什么是Promise
实现Promise
- 确定需求:实现Promise
- 确定要用到的工具和依赖:
mocha(测试框架)
chai(断言库)
sinon(用于模拟一些函数)
ts-node(运行ts文件)
typescript(支持ts)
yarn add mocha chai sinon sinon-chai ts-node typescript -D
yarn add @types/mocha @types/chai @types/sinon @types/sinon-chai -D
mocha -r ts-node/register test/index.ts(可以让mocha用ts-node运行)
测试用例 (可跳过直接看源码)
满足这些条件,代码也就写完了
- Promise是一个类
- Promise()如果接受的不是一个函数就报错
- new Promise()返回的对象是有then方法的
- new Promise(fn)中的fn是立即执行的
- new Promise(fn) 中的fn接受两个函数
- promise.then(success)中的success 会在resolve被调用时执行
- promise.then(null,failed)中的failed会在reject被调用时执行
- promise.then(false,null) then中接受的不是函数会忽略,不报错
- promise(resolve,reject)中的resolve,reject只会被调用一次
- resolve拿到的结果与then成功的回调中的结果一致,reject拿到的结果与then中失败回调中的一致
- 在代码执行完之前不得执行then中的回调
- resolve,reject如果不穿this,默认为undefined
- then可以被调用多次,并且按顺序调用
- 返回一个promise
import * as chai from 'chai'
import * as sinon from 'sinon'
import * as sinonChai from 'sinon-chai'
import Promise2 from '../src'
chai.use(sinonChai)
const assert = chai.assert
describe('Promise', () => {
it('它是一个类', () => {
assert.isFunction(Promise2)
assert.isObject(Promise2.prototype)
})
it('Promise()如果接受的不是一个函数就报错', () => {
assert.throw(() => {
//@ts-ignore
new Promise2()//这里期待报错,不穿参会报错哦
// new Promise2(() => { })//如果你写成这样test就过不了
})
assert.throw(() => {
//@ts-ignore
new Promise2(1)
})
})
it('newPromise()返回的对象是有then方法的', () => {
const promise = new Promise2(() => { })
assert.isObject(promise)
assert.isFunction(promise.then)
})
it('newPromise(fn)接受的fn是立即执行的', () => {
const fn = sinon.fake()
new Promise2(fn)
assert(fn.called)
})
it('new Promise2(fn)fn接受两个函数', () => {
new Promise2((resolve, reject) => {
assert.isFunction(resolve)
assert.isFunction(reject)
})
})
it('promise.then(success,reject)中的success会在resolve调用时执行', () => {
const succeed = sinon.fake()
const promise = new Promise2((resolve, reject) => {
assert(succeed.notCalled)
resolve()
setTimeout(() => {
assert(succeed.called)
})
})
promise.then(succeed)
})
it('promise.then(null,failed)中的failed会在reject调用时执行', (done) => {
const failed = sinon.fake()
const promise = new Promise2((resolve, reject) => {
assert(failed.notCalled)
reject()
setTimeout(() => {
assert(failed.called)
done()
})
})
promise.then(null, failed)
})
it('promise.then(false,null) then中接受的不是函数会忽略,不报错', () => {
const promise = new Promise2((resolve, reject) => {
resolve()
})
promise.then(false, null)
})
it('promise(resolve,reject)中的resolve,reject只会被调用一次', () => {
const succeed = sinon.fake()
const promise = new Promise2((resolve, reject) => {
resolve()
resolve()
setTimeout(() => {
assert(succeed.calledOnce)
})
})
promise.then(succeed)
})
it('resolve拿到的结果与then成功的回调中的结果一致,reject拿到的结果与then中失败回调中的一致', () => {
const promise = new Promise2((resolve, reject) => {
resolve(123)
})
promise.then((res) => {
assert(res === 123)
})
})
it('在代码执行完之前不得执行then中的回调', (done) => {
const succeed = sinon.fake()
const promise = new Promise2((resolve, reject) => {
resolve()
})
promise.then(succeed)
assert(succeed.notCalled)
setTimeout(() => {
assert(succeed.called)
done()
})
it('resolve,reject如果不穿this,默认为undefined', (done) => {
const promise = new Promise2((resolve, reject) => {
resolve()
})
promise.then(function () {
'use strict'
assert(this === undefined)
done()
})
})
})
it('then可以被调用多次,并且按顺序调用', (done) => {
const succeed = [sinon.fake(), sinon.fake(), sinon.fake()]
const promise = new Promise2((resolve, reject) => {
resolve()
})
promise.then(succeed[0])
promise.then(succeed[1])
promise.then(succeed[2])
setTimeout(() => {
assert(succeed[0].called)
assert(succeed[1].called)
assert(succeed[2].called)
assert(succeed[1].calledAfter(succeed[0]))
assert(succeed[2].calledAfter(succeed[1]))
done()
})
})
it('then返回一个Promise,可以连续then并且可以把结果往后传递', (done) => {
const promise = new Promise2((resolve, reject) => {
resolve()
})
const promise2 = promise.then(() => '成功').then(res => {
assert.equal(res, '成功')
done()
})
assert(promise2 instanceof Promise2)
})
it('then中返回的promise 可以结果成功会走resolve/失败可以走reject', (done) => {
const fn = sinon.fake()
const promise1 = new Promise2((resolve, reject) => {
resolve()
})
const promise2 = promise1.then(() => new Promise2(resolve => resolve()))
promise2.then(fn)
setTimeout(() => {
assert(fn.called)
done()
})
})
})
promise代码
//nextTick可以换成setTimeout,这里为了写测试用例用了优先级更高的MutationObsever
class Promise2 {
callbacks = []
status = 'pending'
resolveOrReject = (status, index, resultOrReason) => {
if (this.status !== 'pending') return;
this.status = status
nextTick(() => {
this.callbacks.forEach(handle => {
if (typeof handle[index] === 'function') {
let x
try {
x = handle[index].call(undefined, resultOrReason)
} catch (e) {
return this.reject(e)
}
handle[2].resolveWith(x)
}
})
})
}
resolve = (result) => {
this.resolveOrReject('fulfilled', 0, result)
}
reject = (reason) => {
this.resolveOrReject('rejected', 1, reason)
}
constructor(fn) {
if (typeof fn !== 'function') {
throw new Error('Promise只接受函数')
}
fn(this.resolve.bind(this), this.reject.bind(this))
}
then(success?, failed?) {
const handle = [];
(typeof success === 'function') && (handle[0] = success);
(typeof failed === 'function') && (handle[1] = failed);
handle[2] = new Promise2(() => { })
this.callbacks.push(handle)
return handle[2]
}
resolveWith(x) {
if (this === x) {
return this.reject(new TypeError())
}
if (x instanceof Promise2) {
x.then((result) => {
this.resolve(result)
}, (reason) => {
this.reject(reason)
})
}
if (x instanceof Object) {
let then
try {
then = x.then
} catch (e) {
this.reject(e)
}
if (then instanceof Function) {
try {
x.then(y => {
y => {
this.resolveWith(y)
}
}, r => {
this.reject(r)
})
} catch (e) {
this.reject(e)
}
} else {
this.resolve(x)
}
} else {
this.resolve(x)
}
}
}
export default Promise2
function nextTick(fn) {//这个函数用来解决setTimeout优先级的问题可以忽略
if (process !== undefined && typeof process.nextTick === "function") {
return process.nextTick(fn)
} else {
var counter = 1
var observe = new MutationObserver(fn)
var textNode = document.createTextNode(String(counter))
observe.observe(textNode, {
characterData: true
})
counter = (counter + 1) % 2
textNode.data = String(counter)
}
}
图解
返回一个Primose
promise的状态改变
Promise 有三种状态:Pending 初始态; Fulfilled 成功态; Rejected 失败态。
- pending变成Fulfilled
- pending变成Rejected 说明:只有这两种,且一个promise对象只能改变一次,无论变为成功还是失败,都会有一个结果数据,成功的结果数据一般称为vlaue,失败的结果数据一般称为reason.
promise的缺点是什么? -cons
如何解决这些缺点? -more
MutationObserver(nextTick实现原理)
//在写测试用例事时因为setTimeout优先级问题,已经不能满足正常测试! 我们可以使用nextTick代替
function nextTick(fn) {
if (process !== undefined && typeof process.nextTick === "function") {
return process.nextTick(fn)
} else {
var counter = 1
var observe = new MutationObserver(fn)
var textNode = document.createTextNode(String(counter))
observe.observe(textNode, {
characterData: true
})
counter = (counter + 1) % 2
textNode.data = String(counter)
}
}