JavaScript异步编程:Promise篇

前言

异步编程作为JavaScript 中的核心内容,是必须要掌握的一个基础。在日常开发过程中,会有很多异步的场景。比如定时器,网络请求,事件监听等等… 而异步编程中也有很多让人诟病的点,比如 callback hell(回调地狱)。
这个系列是记录下自己平常对异步知识的一些思考和记录。之后还会有对generator函数以及async/await的文章。

正文

这一篇文章不是关注promise API的使用,而是根据promise/A+的规范来实现一个promise。由于涉及到具体实现,需要有一定的JavaScript基础。

规范地址

中文版
英文版

具体实现

/**
	自定义Promise实现,遵循Promise/A+规范

*/
var PENDING = 'pending';
var FULFILLED = 'fulfilled';
var REJECTED = 'rejected';

function Promise(fn){
	var self = this;

	if (!(this instanceof Promise)){
		throw new Error('must use a new operator symbol!');
	}

	if (!fn) {
		throw new Error('a param is required!');
	}

	if (typeof fn !== 'function') {
		throw new Error('the param must be a function!');
	}

	this.status = PENDING;
	this.value = undefined;
	this.reason = undefined;


	//异步事件队列
	this.resolveQuene = [];
	this.rejectQuene = [];

	// 触发成功回调
	var resolveAction = function(param){

		if (self.status === PENDING) {
			self.status = FULFILLED;
			self.value = param;
			// 执行成功回调方法队列
			for (var i = 0;i < self.resolveQuene.length;i++) {
				self.resolveQuene[i] && self.resolveQuene[i]();
			}
		}

	}

	// 触发失败回调
	var rejectAction = function(reason){

		if (self.status === PENDING) {
			self.status = REJECTED;
			self.reason = reason;

			// 执行失败回调方法队列
			for (var i = 0;i < self.rejectQuene.length;i++) {
				self.rejectQuene[i] && self.rejectQuene[i]();
			}
		}

	}

	try {
		fn.call(null,resolveAction,rejectAction);
	} catch(e){
		rejectAction(e);
	}
}



Promise.resolve = function(param){
	return new Promise(function(resolve){
		resolve(param);
	});
}

Promise.reject = function(reason){
	return new Promise(function(resolve,reject){
		reject(reason);
	});
}

Promise.prototype.catch = function(onRejected){
	return this.then(null,onRejected);
}

Promise.all = function(promiseArray){
	if (!Array.isArray(promiseArray)) {
		throw new Error('param must be a array');
	}
	var store = [];
	return new Promise(function(resolve,reject){
		for (var i = 0; i < promiseArray.length; i ++) {
			promiseArray[i].then(function(value){
				store.push(value);
				if (store.length === promiseArray.length) {
					resolve(store);
				}
			},function(reason){
				reject(reason);
			});
		}
	});
}

Promise.race = function(promiseArray){
	if (!Array.isArray(promiseArray)) {
		throw new Error('param must be a array');
	}
	return new Promise(function(resolve,reject){
		for (var i = 0; i < promiseArray.length; i ++) {
			promiseArray[i].then(function(value){
				resolve(store);
			},function(reason){
				reject(reason);
			});
		}
	});
}

Promise.assistPromise4resolve = function(promise,x,resolve,reject){
	if (x === promise) {
		throw new TypeError('chaning Promise error');
	} else if (x !== null && (typeof x === 'object' || typeof x === 'function')){
		let flag = false;
		try {
			var then = x.then;
			if (typeof then === 'function') {
				then.call(x,function(y){
					if (flag) return;
					flag = true;
					Promise.assistPromise4resolve(promise,y,resolve,reject);
				},function(reason){
					if (flag) return;
					flag = true;
					reject(reason);
				})
			} else {
				if (flag) return;
				flag = true;
				resolve(x);
			}
		} catch(e) {
			if (flag) return;
			flag = true;
			reject(e);
		}
		
	} else {
		resolve(x);
	}
}


Promise.prototype.then = function(onFulfilled,onRejected){
	// promise 规范 : 每一个then方法必须要返回一个新的promise以支持链式调用
	// 为什么不直接返回一个 this 呢?
	// 1.因为promise的状态只能改变一次
	
	// 指向上一个promise的实例
	var self = this;

	/*
		如果 onFulfilled 或者 onRejected 返回一个值 x ,则运行下面的 Promise 解决过程:[[Resolve]](promise2, x)
		如果 onFulfilled 或者 onRejected 抛出一个异常 e ,则 promise2 必须拒绝执行,并返回拒因 e
		如果 onFulfilled 不是函数且 promise1 成功执行, promise2 必须成功执行并返回相同的值
		如果 onRejected 不是函数且 promise1 拒绝执行, promise2 必须拒绝执行并返回相同的据因
	*/
	var nextPromise =  new Promise(function(resolve,reject){
		if (self.status === FULFILLED) {
		// 为了确保代码的逻辑顺序,规范要求then的回调要异步执行
			setTimeout(function(){
				try {	
					if (typeof onFulfilled === 'function') {
						var innerValue = onFulfilled(self.value);
						Promise.assistPromise4resolve(nextPromise,innerValue,resolve,reject);
					} else {
						resolve(self.value);
					}
				} catch (e){
					reject(e)
				}
			});
		}
		
		if (self.status === REJECTED) {
			// 为了确保代码的逻辑顺序,规范要求then的回调要异步执行
			setTimeout(function(){
				try {
					if (typeof onRejected === 'function') {
						var innerReason = onRejected(self.reason);
						Promise.assistPromise4resolve(nextPromise,innerReason,resolve,reject);
					} else {
						reject(self.reason);
					}
					
				} catch (e) {
					reject(e);
				}
				
			});

		}

		// 假如实例化promise时执行的是异步代码,此时状态还是pending
		// 为了确保代码的逻辑顺序,规范要求then的回调要异步执行

		if (self.status === PENDING) {
			self.resolveQuene.push(function(){
				setTimeout(function(){
					try {	
						if (typeof onFulfilled === 'function') {
							var innerValue = onFulfilled(self.value);
							Promise.assistPromise4resolve(nextPromise,innerValue,resolve,reject);
						} else {
							resolve(self.value);
						}
					} catch (e){
						reject(e)
					}
				})
			});

			self.rejectQuene.push(function(){
				setTimeout(function(){
					try {
						if (typeof onRejected === 'function') {
							var innerReason = onRejected(self.reason);
							Promise.assistPromise4resolve(nextPromise,innerReason,resolve,reject);
						} else {
							reject(self.reason);
						}
						
					} catch (e) {
						reject(e);
					}
				});
			});
		}
	});
	
	return nextPromise;

}



module.exports = Promise;

如何验证这个promise的准确性呢,这里有一个cli工具,定义了800多个测试用例,可以下载下来测试。

npm install promises-aplus-tests -D

注意为了测试这个promise的实现,需要加上这么一段代码供这个工具调用

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

接下来运行一下这个测试工具。

 npx promises-aplus-tests ./promise.js 

可以看到下面的结果,是可以通过所有测试用例的。

在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章