Skip to content

Files

Latest commit

 

History

History
138 lines (101 loc) · 3.77 KB

04.md

File metadata and controls

138 lines (101 loc) · 3.77 KB

从零开始编写自己的JavaScript框架(四)

#4. 异步编程与Promise

##4.1 异步编程有什么不同

异步编程有什么样的特点呢?比如说你有一个异步方法a,如果想要在a执行完之后,再执行一个b:

a();
b();

这样基本是得不到期待的结果的,很可能a里面的内容没有执行完,b倒先做完了。

通常我们写异步调用,都是使用回调的方式,比如这样:

function a(success, fail) {
	//监听某个事件,检测到成功,就调用success,检测到失败,就调用fail	
}

a(function(data) {
	console.log(data);
}, function(reason) {
	console.log(reason);
});

但是回调有个缺点,如果几个操作是有依赖关系,比如b要等到a成功了再做,c要等到b成功了再做,那代码可能变成这样:

a(function(data) {
	b(function(data1) {
		c(function(data2) {
			//c成功之后要做的事情
		})
	})
});

这个写法很令人头疼,才3个连环操作,代码就变成这样了,多一个操作就多一层,到最后代码基本没法看了。

现在我们的需求很明确:

  • 按照什么顺序写的代码,我就希望它按照什么顺序执行
  • 不要搞成上面那样的嵌套

再一次拔剑四顾心茫然,什么才是我们真正要的东西?如果语言本身书写和执行的顺序不能控制,那我们可以人为创造一个队列。我们联想到jQuery,它以链式表达式著称,比如:a().b().c()这样,如果我们只是这样,那这个代码还是不对,跟三个函数分开写没有任何区别,因为没有实现异步。

这好办,异步就是让后续的函数不立即执行,换言之,我只要把这些后续函数存在某个地方,然后按照在它们的先决条件执行完之后才拿出来执行,就可以了,如果这样呢?when(a).then(b).then(c);

这个看上去还可以,所以我们来打算实现它。

#4.2 Promise

先看看要怎么实现方法的顺序调用,一定是要有一个地方能存放这个调用序列的,比如说是个队列。

  • 每次有新方法要执行的时候,如果当前正在执行其他方法,就先把这个方法放到队列尾部,否则立即执行,并且把当前状态置为执行中。
  • 在执行完成之后,修改执行状态为空闲,从队列头取出下一个方法,如果非空继续执行这个方法。

好,我们开始写:

var queue = [];
var executing = false;

function then(foo) {
	if (executing) {
		queue.push(foo);
	}
	else {
		foo(function() {
			executing = false;
			next();
		});
	}	
}

function next() {
	if (queue.length > 0) {
		var foo = queue.shift();
		foo(function() {
			executing = false;
			next();
		});
	}		
}

然后看看怎么写:

then(a);
then(b);
then(c);

哎,不对啊,我们不是要这么写,而是要形成一个链呢。再来:

function ExecuteQueue() {
	this.queue = [];
	this.executing = false;
}

ExecuteQueue.prototype = {
	then: function(foo) {
		if (this.executing) {
			this.queue.push(foo);
		}
		else {
			var that = this;
			foo(function() {
				that.executing = false;
				that.next();
			});
		}

		return this;	
	},

	next: function() {
		if (this.queue.length > 0) {
			var foo = this.queue.shift();
			var that = this;
			foo(function() {
				that.executing = false;
				that.next();
			});
		}		
	}	
};

用的时候,这样:

var queue = new ExecuteQueue();
queue.then(a).then(b).then(c);

好像有那么回事了,但还缺一些东西,比如说,如果a出异常了,想要取消b和c的执行,怎么办?这里面的问题在什么地方呢?队列可以调度这些方法的执行过程,但是这些方法本身没法控制队列的状态。

function a() { var queue = new ExecuteQueue();

return queue;

}