大话Promise系列之二:学几个术语

老姚
老姚 发布于 2016-11-21 20:00:28 浏览:669 类型:原创 - 随笔 分类:JavaScript - 我也来说说系列 二维码: 作者原创 版权保护
看了两本书相关书籍后,准备写几篇笔记。
望读者要求别太高,就当乐呵看。emoticon
参考的书籍是:
1.http://es6.ruanyifeng.com/#docs/promise
2.http://liubin.org/promises-book/
3.《你不知道的JavaScript中卷》、《英文版》

本文是第二篇。
第一篇文章对相关术语说的不详细,本文详细说说。
先看一下下面的代码:
var p = new Promise(function(resolve, reject) {
        resolve(20);
});
p.then(
        function onFulfilled() {},
        function onRejected() {}
);

1.基本术语

其中promise表示承诺,即对某一件事情的承诺:这个事情,交给我了,你放心吧。
然而此承诺是有状态的,进行中、已完成、已拒绝。
其中已完成是指,事情办成了,而拒绝是指,事情办砸了。
不论是办成还是办砸,那么此事情都是办完了。
办完了,就是说此承诺已决议了。
而得到承诺的人,会对承诺的决议结果(完成或拒绝)会所响应,未决议是不会相应的。

这里的几个名词有对应的英文,
状态:进行中(pending)、已完成(fulfilled)、已拒绝(rejected)
已决议(resolved):已完成、已拒绝
未决议:进行中
响应:完成回调(onFulfilled)、拒绝回调(onRejected)

注:状态可以通过console.log查看其[[PromiseStatus]]属性值。
参考文献一上面认为,已完成也用resolved单词表示,浏览器打印的结果确实是这样的。
本文更偏向于参考文献3的观点,认为resolve是决议,状态结果是包含完成的,个人觉得这样会更好理解一些。请看下面的论述。

2.resolve是决议不只是完成

看上面的代码,我们发现,构造函数的参数(函数)的参数传递的名字是resolve和reject,并不是fulfill和reject。
虽然参数名字可以随便起,但是用resolve而不是fulfill是有用意的。
这里我们把resolve和reject当作动词来用
调用了reject表示,状态进入已拒绝。
调用resolve表示,状态结果需要进一步决议,最终结果可能是未决议,也可能是已决议(已完成、已拒绝)。


这里有必要再强调的一点是单词resolve,是决议而不是完成的意思。看如下的代码:
<script>
var p = new Promise(function(resolve, reject) {
	resolve(Promise.reject('no!'));
});
p.then(
	function onFulfilled() {}, 
	function onRejected(msg) {
		alert(msg);
	}
);
</script>

上面的代码中,构造函数内部虽然调用的是resolve,但是then方法注册的拒绝回调触发了。

所以Promise.resolve其api的含义不是类似于Promise.reject那样,
直接生成一个状态为完成的promise实例。

而是说看参数情况,我们决议一下吧:
<script>
var p = Promise.resolve(30);
p.then(
	function (value) {
		alert(value);
	}, 
	function () {}
);
</script>

上面走的是完成回调,说明p的状态决议成了完成。

如果参数已经是promise实例的话,直接返回该实例:
<script>
var p = Promise.resolve(new Promise(function(resolve, reject) {
	reject('no!')
}));
p.then(
	function () {
	}, 
	function (msg) {
		alert(msg);
	}
);
</script>

当然上述实例p的决议状态是拒绝。

如果传进是thenable,会封装成promise实例:
<script>
var o = {
	then: function(resolve, reject) {
		reject('no');
	}
};
var p = Promise.resolve(o);
p.then(
	function () {
	}, 
	function (msg) {
		alert(msg);
	}
);
</script>

来个一直等待中的情形。
<script>
var p = Promise.resolve(new Promise(function(resolve, reject) {
	alert('unresolved');
}));
p.then(
	function () {
		alert('fulfilled')
	}, 
	function () {
		alert('rejected');
	}
);
</script>

其实,我们可以这么想一下,某人求我办件事情,我转而拜托另一个人,进而把他给我的承诺抛给了之前的那个某人。
resolve表示决议的结果取决于另一个人,他可能完成,可能拒绝,甚至也可能一直处于进行中。


3.完成值和拒绝值
不管决议的结果是完成还是拒绝,都会相应的拿到最终的值。
比如上面的30是完成值,no!是拒绝值。
完成值,总是需要编程给出的,而拒绝值一般也称为拒绝原因。
拒绝值,不一定需要编程给出:
<script>
var p = new Promise(function(resolve, reject) {
	throw new Error('no!');
});
p.then(
	function () {
	}, 
	function (msg) {
		alert(msg);
	}
);
</script>

上面是直接抛出异常,假如是语法错误呢:
<script>
var p = new Promise(function(resolve, reject) {
	xxx
});
p.then(
	function () {
	}, 
	function (msg) {
		alert(msg);
	}
);
</script>

看到resolve调用时,可以传进promise实例,那么reject也传一下试试:
<script>
var p = new Promise(function(resolve, reject) {
	reject(Promise.resolve(2));
});
p.then(
	function () {
	}, 
	function (msg) {
		alert(msg);
	}
);
</script>

证明了,reject传进的东西,只会当成拒绝值,而不像resolve有递归的味道在里面。

注:此值可以通过console.log查看其属性[[PromiseValue]]。

4.只能决议一次
决议的结果,要么不决议(未决议,pending),要么完成,要么拒绝,只能3选一。
<script>
var p = new Promise(function(resolve, reject) {
	resolve(2);
	reject('no!');
});
p.then(
	function (value) {
		alert(value);
	}, 
	function (msg) {
		alert(msg);
	}
);
</script>

上面的代码中,可以看出如果一开始决议是完成的话,那么后面的拒绝就没有用了。

关于决议只能有一次机会,通过点击按钮决议能更好的说明:
<button id="success">success</button>
<button id="failure">failure</button>
<script>
	var s = document.querySelector('#success');
	var f = document.querySelector('#failure');
	var resultOfPromise = new Promise(function(resolve, reject) {
		s.onclick = function() {
			resolve();
		};
		f.onclick = function() {
			reject();
		};
	});
	resultOfPromise.then(
		function() {
			alert('success');
		},
		function() {
			alert('failure')
		}
	);
</script>

如果你知道构造函数的参数(是个函数)是同步执行的话,
可能会想我在外边并且异步决议的话,是否也是这样的,答案仍然是的。
<script>
var outer = {};
var p = new Promise(function(resolve, reject) {
	outer.resolve = resolve;
	outer.reject = reject;
});
p.then(
	function (value) {
		alert(value);
	}, 
	function (msg) {
		alert(msg);
	}
);
setTimeout(function() {
	outer.resolve(2);
	outer.reject('no');
	outer.resolve(3);
}, 1000);
</script>

其实这种写法有点像Deferred了。
这个第4点,个人觉得是promise的最重要一点。


总结

本文介绍了promise相关的概念。
思想来源是《你不知道的javascript》中卷。
于第一篇相比,没有说太多新的东西。
个人觉得本文说明白了,很多人都没讲清楚的问题,resolve这个单词很重要的。

本文完。

z
给个赞 12 人点赞
收藏 11 人收藏
评论 已有 2 条评论;以下用户言论只代表其个人观点,不代表 前端网(QDFuns) 的观点或立场。
登录 以后才能发表评论
最新评论
hbzhanpeng
hbzhanpeng2016-11-23 09:39:462F
emoticon生动形象啊
举报 支持 (0) 回复 (0)
乔布斯
乔布斯2016-11-22 00:03:471F
emoticon
举报 支持 (0) 回复 (0)
老姚 老姚 作者

与自己为敌,与自己为友,一边深挖思想,一边埋葬自己。

作者最新