当前位置: 首页 > 工具软件 > node-promise > 使用案例 >

深入浅出NodeJS笔记(五)---- Promise的序列执行

莘翰采
2023-12-01
书中第四章异步编程介绍的内容比较丰富,今天读到支持序列执行的Promise一节,为加深理解,将代码和体会整理如下。
让Promise支持序列执行的初衷是减少编码量。如书中所言,直接使用回调函数嵌套或者事件监听(Emitter)会导致大量的重复或者丑陋代码。而Promise有then方法,如果级联起来,代码逻辑清楚,内容简洁。
首先,定义Promise和Deferred对象:
var Promise = function(){
	this.thenQueue = [];
	this.isPromise = true;
};

var Deferred = function(){
	this.promise = new Promise();
};
这里的thenQueue用于保存then方法中的回调函数,以便在后面序列执行。然后定义Promise的then方法:
Promise.prototype.then = function(fulfilledHandler){
	var handler = {};
	if(typeof fulfilledHandler === 'function'){
		handler.fulfilled = fulfilledHandler;
	}
	this.thenQueue.push(handler);//放入回调函数队列
	return this;//返回自身用于链式调用
};

为了简单起见,这里只实现了完成态的情况,不考虑出错失败态的情况。then方法返回Promise自身用于链式调用。然后定义Deferred的生成callback的工具函数:
Deferred.prototype.callback = function(){
	var that = this;
	return function(value){
		that.resolve(value);
	};
}

该函数为异步调用生成一个回调函数,该回调会触发Deferred的resolve方法,同样未考虑reject的情况。最后定义Deferred的resolve方法:
Deferred.prototype.resolve = function(val){
	var promise = this.promise;
	var handler;
	while((handler = promise.thenQueue.shift())){
		if(handler && handler.fulfilled){
			var res = handler.fulfilled(val);
			if(res && res.isPromise){
				res.thenQueue = promise.thenQueue;//将queue中剩余回调转交给后续promise
				this.promise = res;
				return;
			}
		}
	}
};

while循环从当前deferred对象的promise维护的回调队列中按顺序取出回调函数并执行,如果回调函数的返回值仍然是Promise,则将当前thenQueue赋值给该返回Promise,并将当前Deferred的promise对象更新为返回的Promise(为后续resolve做准备),然后立即返回(因为要按序执行,刚刚返回的promise还没有resolve,不能继续处理后续then的回调)。如果回掉函数的返回值不是Promise,则继续处理后续的then回调函数。下面举个例子:
var getValue = function(name, callback){
	var map = {
		'cat': 'july',
		'july': 'jay'
	};
	setTimeout(function(){
		callback(map[name]);
	}, 500);
};
首先定义一个函数模拟异步调用,该函数通过一个name获取一个value,500毫秒返回结果。然后定义两个函数调用getValue方法,要求第二个函数调用依赖于第一个函数调用的返回结果,如:
var testValue1 = function(name){
	var deferred = new Deferred();
	getValue(name, deferred.callback());
	return deferred.promise;
};

var testValue2 = function(name){
	var deferred = new Deferred();
	getValue(name, deferred.callback());
	return deferred.promise;
};

testValue1('cat').then(function(value1){
	//第二次调用依赖于第一次的结果
	return testValue2(value1);
}).then(function(value2){
	//输出'jay'
	console.log(value2);
}).then(function(){
	//该调用不依赖于前面结果,但也需等到前面调用完毕才能执行
	console.log('Hello Cat');
});

testValue1异步调用结束后获得value1,然后调用testValue2,并传入value1。testValue2返回一个Promise,并进一步返回给resolve方法中的res变量,按照前面所述逻辑,此时testValue2放回的Promise维护的队列更新为新的队列(里面除去第一个then回调,还有两个函数待执行)。当第二个getValue结束后,输出结果'jay',没有返回值。因此在resolve方法中res的值是undefined,while循环会继续执行,取出最后一个then回调,执行之输出'Hello Cat',此时仍无返回值,且队列为空,程序结束。
另外,虽然后两次then回调没有返回Promise,但是如果后续调用依赖于testValue2的结果value2,仍然可以继续调用then方法,如:
testValue1('cat').then(function(value1){
	//第二次调用依赖于第一次的结果
	return testValue2(value1);
}).then(function(value2){
	//输出'jay'
	console.log(value2);
}).then(function(){
	//该调用不依赖于前面结果,但也需等到前面调用完毕才能执行
	console.log('Hello Cat');
}).then(function(value2){
	//仍然输出'jay'
	console.log(value2);
});

可以这样的做的原因是当前while循环仍然是resolve value2时的循环,依次在执行回调,value2依然可见。因此,如果后续调用不返回新的Promise,则一直可以依赖value2。而value1已经不可见,这是由调用的依赖关系决定的。
这种模式的好处是,简单明了地定义了多个异步调用顺序执行的逻辑,但即使两个调用之间没有依赖关系,仍然是顺序执行(如后两个then回调)。


 类似资料: