都2020了,也该会自己手写一个promise了

942 阅读5分钟

写在前面

今天在看文章都时候发现了手写promise的要求,扪心自问,我靠,我居然一点思路都没有。这可不行,必须自己手撸一发,面的对不起自己的左右手。哎,又要学习,好累,不过还好,想想都2020了,还不会自己手写promise,着实有点垃圾,还是赶快搞起来吧。


promise的几点要求

  1. 什么是promise?
  2. promise解决的什么问题?
  3. 我们自己实现一个promise。

什么是promise?

Promise是一种异步操作的解决方案,将写法复杂的传统的回调函数和监听事件的异步操作,用同步代码的形式表达出来。避免了多级异步操作的回调函数嵌套。简单的来说就是为了解决回调地狱的问题。试想一下,下面这个代码你看这头疼不?如果在加几层呢就会出现金字塔的代码了,这样给我们阅读代码和维护代码造成的很大的困扰。

function a() {
  function b() {
    function c() {
      function d() {}
      d();
    }
    c();
  }
  b();
}
a();

promise解决了什么问题?

我们将上面的代码用promise改写。这样是不是好多了。

a().then(b).then(c).then(d)

实现一个Promise

我们按照promiseA+的规范来写。不知道的同学可以看看哦。

Promise类

const PENDING = pending
const FULFILLED = fulfilled
const REJECTED = rejected
class Promise {
	constructor(exextuor) {
    	this.exextuor = exextuor
    	this.states = PENDING
    	this.onResolveCallbacks = []
    	this.onRejectCallbacks = []
	}
	function resolve() {}
	function reject() {}
	
	if(exextuor) {
        try {
    		exextuor(resolve, reject)
    	}catch(e) {
    		reject(e)
    	}
	}
}

上面的代码很简单,我们一个resolve的调度栈,一个reject的调度栈,声明resolve和reject函数,并执行exextuor如果报错,将reject执行。

resolve和reject函数(2.1规范)

一个 promise 有且只有一个状态(pending,fulfilled,rejected 其中之一) pending 状态时可能会转变为 fulfilled 或 rejected 状态。fulfilled 状态时:不能再状态为任何其他状态,必须有一个 value,且不可改变。 rejected 状态时:不能再状态为任何其他状态,必须有一个 reason,且不可改变。

function resolve(value) {
	if(value instanceof Promise) {
        try{
    	    value.then(resolve, reject)
    	}catch(e) {
    		reject(e)
    	}
	}
	if(this.states = PENDING) {
	    this.states = FULFILLED
        this.value = value
	    this.onResolveCallbacks.forEach(cb => cb(this.value))
	}
}
function reject(reason) {
	if(this.states = PENDING) {
		this.states = REJECTED
		this.reason = reason
		this.onRejectCallbacks.forEach(cb => cb(this.reason))
	}
}

then函数(2.2规范)

一个 promise 必须提供一个 then 方法,用来获取当前或最终的 value 或 reason,一个 promise 的 then 方法接受两个参数:promise.then(onFulfilled, onRejected)

then(onFulfilled, onRejected) {
	onFulfilled = typeof onFulfilled === "function" ? onFulfilled : value => value
	onRejected = typeof onRejected === "function" ? onRejected : reason => throw reason
	if(this.states = FULFILLED) {
		this.x = onFulfilled(this.value)
	}
	if(this.states = REJECTED) {
		this.x = onRejected(this.reason)
	}
	if(this.states = PENDING) {
		this.onResolveCallbacks.push(() => {
			this.x = onFulfilled(this.value)
		})
		this.onRejectCallbacks.push(() => {
			this.x = onRejected(this.reason)
		})
	}

then函数的链式调用

promise 的 then 可以链式调用多次,如果或当 promise 转态是 fulfilled 时,所有的 onFulfilled 回调回以他们注册时的顺序依次执行。如果或当 promise 转态是 rejected 时,所有的 onRejected 回调回以他们注册时的顺序依次执行。

let primise2
if(this.states = FULFILLED) {
	return primise2 = new Promise((resole, reject) => {
		// 如果在执行onFulfilled的时候有异常我们要将primise2的reject执行
		try {
			let x = onFulfilled(this.value)
			// 对返回值进行处理,因为这个值可能是个promise
			resolvePromise(primise2, x, resole, reject)
		} catch(e) {
			reject(e)
		}
	})
}

注意上面的三个判断的处理基本一样,我们都要对结果处理。resolvePromise一下。

resolvePromise(2.3规范)

2.3.1 如果返回的 promise1 和 x 是指向同一个引用(循环引用),则抛出错误 2.3.2 如果 x 是一个 promise 实例,则采用它的状态

  1. 如果 x 是 pending 状态,那么保留它(递归执行这个 promise 处理程序),直到 pending 状态转为 fulfilled 或 rejected 状态。
  2. 如果或当 x 状态是 fulfilled,resolve 它,并且传入和 promise1 一样的值 value。
  3. 如果或当 x 状态是 rejected,reject 它,并且传入和 promise1 一样的值 reason
const PENDING = pending
const FULFILLED = fulfilled
const REJECTED = rejected

function resolvePromise(promise2, x, resolve, reject) {
	if(promise2 === x) {
		return reject(new TypeError('循环引入!'))
	}
	
	// 用于resolve, reject只调用一次
	let called = false
	
	// x是一个promise
	if(x instanceof Promise) {
		if(x.status === PENDING) {
			x.then((y)=>{
				// 有可能y还是一个promise
				resolvePromise(promise2, y, resolve, reject)
			}, reject)
		} else {
			x.then(resolve, reject)
		}
	} else if(x !== null && (typeof x === 'function' || typeof x === 'object')) {
	// x是一个thenable的对象
		try{
			// 有可能在区x.then的时候异常
			let then = x.then
			if(typeof x === 'function') {
				// 这是我们自己的promise和别人的promise的交互
				then.call(x, (z)=>{
					// 有可能z还是一个promise
					if(called) return
					called = true
					resolvePromise(promise2, z,resolve, reject)
				}, (err)=>{
					if(called) return
					called = true
					reject(err)
				})
			}else {
				// 说明x是一个普通对象
				resolve(x)
			}
		}catch(e) {
			reject(e)
		}
	} else {
		// x是个普通值
		if(called) return
		called = true
		resolve(x)
	}
}

catch函数

catch(onReject) {
	this.then(null, onReject)
}

异步调用(3.1规范)

3.1 这里的 “平台代码”是指引擎,环境,和 promise 实现代码。实际上,这个要求确保 onFulfilled 和 onRejected 都在下一轮的事件循环中(一个新的栈)被异步调用。可以用宏任务,例如:setTimeout,setImmediate 或者微任务,例如:MutationObsever 或 process.nextTick 实现。 由于 promise 的实现被当做平台代码,所以它本身可能包含一个任务队列或 “trampoline” 的处理程序

// 所以我们要在所有resolve和reject的调用上加一个setTimeout()
// 我们在resolve函数外面包裹一个
// 同理我们也要在reject函数包裹一下
function resolve() {
	setTimeout(()=>{
		// 上面的内容一样
	})
}

promise.all

Promise.all = function(promises) {
	return new Promise((resolve, reject)=>{
		let res = []
		let count = 0
		function done(i, result) {
			res[i] = result
			if(++count === promises.length) {
				resolve(res)
			}
		}
		for(let i = 0; i<promises.length; i++) {
			done.call(null, i,res)
		}
	}, reject)
}

测试结果

Promise.deferred = Promise.deferred = function() {
	let defer = {}
	defer.promise = new Promise((resolve, reject) => {
		defer.resolve = resolve
		defer.reject = reject
	})
	return defer
}

我们测试用用例测试。基本所有的用例全部通过。好了这样一个promise基本实现。