当我们说 Promise ,我们在说什么?(刨根向)
发布于 8 年前 作者 captainblue2013 3629 次浏览 来自 分享

原文地址:开发者小蓝

当我要组织文章内容我时候,我感到十分的吃力。

这是源于一个困惑:我们现在是否还需要探讨什么是Promise

我们很容易就能 “使用” Promise,已经有很多优秀的模块实现了不同标准的Promise。 而随着ES6原生 Promise 的落实,我们更容易写出 Promise 风格的异步代码。

// ES6 下的原生Promise
var httpGet = (url) => {
	return new Promise( (resolved,rejected)=>{
		request.get(url,(err,res)=>{
			if(!err){
				resolved(res);
			}else{
				rejected(err);
			}
		});
	});
}

httpGet('http://abc.com').then(...);

但是,治学这东西,知其然,还要知其所以然。

尤其是现在很多声音还存在对 Promise 的曲解的情况下。

那么,Promise到底是什么东西?

有一种说法是用了Promise就不需要callback的写法了,实际上是不是这样,我们看一段代码

	func(arg ,(err,ret)=>{
		//pass
	});
	
	func(arg).then( (err,ret)=>{
		//pass
	});

实际上只是callback写的位置不一样而已,并没有什么实际的改变。

是不是这样呢?我们层层深入地理解一下 Promise 到底是什么。

对于上面这个问题, Promise 是不是只是把callback换个地方写呢?

我从《深入浅出Nodejs》 4.3.2 中的一段话收到了启发

上面的异步调用中,必须严谨地设置目标。那么是否有一种先执行异步调用,延迟传递处理的方式呢?

说的就是传统的callback写法,必须在异步调用的时候,明确地声明回调处理的逻辑。

httpGet('/api',{
	success:onSuccess,
	error:onError
});

就是说,你必须明确指明了异步调用结束时的各种回调,然后才会执行异步操作

(声明异步操作)---> (注册回调) ---> (执行异步操作)

反过来看看 Promise 是不是就不一样呢?先看一段代码

var p = httpGet('/api');

p.then(onSuccess , onError);

你猜到底在哪一步发起了http请求?

正如你猜测的一样,在 p 初始化的时候,这个异步操作就执行了。 所以对于Promise来说,流程是这样的

 (声明异步操作)---> (执行异步操作) ---> (注册回调)

原来真的存在一种方法,先执行异步调用,延迟传递处理的方式。这就是Promise与传统callback一个很显著的区别。

状态机

如果 Promise 只是对callback在逻辑顺序及书写方式上面的一点改动的话,

那你就小看它了。

有没有想过,为什么 Promise 能先执行异步操作,再指明回调逻辑呢?下面这段代码又会如何?

var p = httpGet('/api');

// do something

p.then(onSuccess , onError);

// do something

p.then(onSuccess , onError);

请问,第二个then是否正常执行 ?

Promise 下的状态

Promise 定义了其内部的几个状态

  • pending
  • resolved (fullfilled)
  • rejected

Promise 初始化后 内部状态为 pending ,然后开始执行异步操作, 当异步操作完成,内部状态转换为 resolvedrejected (失败时)。

一旦状态改变,就会被固化,不会再改变。

所以就很好解释,一个状态已经确定的 Promise ,无论你调动多少次then,它都会返回正确且唯一的结果,因为 Promise的结果,是完全依赖它自己的内部状态。

这个时候我们有必要说一下then 方法到底做了什么?

不去深入探究的话,你大概会认为then只是注册了一系列回调函数。

其实 then 除了注册回调函数,还会检查 Promise 状态,只要不是pending状态,就回调用相应状态的回调。

同样的,当状态改变的时候,也会检查对应状态是否有已经绑定的回调函数,再按照Promise的方式去执行回调。

正因为这种机制,才能真正实现了Promise的异步操作与回调声明分离,并且通过维护状态变化,更好地控制异步操作结果中的不同情况。

注:为了降低理解成本,本文的实现里一个状态默认只处理一个回调函数

一字长蛇阵

以上便是一个独立的 Promise 的运行机理,在这之上灵活运用才是Promise的终极玩法。

那么我们经常听到的用 Promise 解决回调地狱,是怎么回事呢?看看最终代码:

loadPic('/path/of/picture.jpg')
	.then(...) //图片压缩
	.then(...) //生成缩略图
	.then(...); //存储到指定地方

想想用callback的时候你是怎么写的。

它的原理就是,每个then的 resolved 部分,返回一个新的 Promise, 这么一来就能继续 then 下去,只要每一个环节都遵守Promise规范,就能将一个回调地狱梳理成串行链式调用

最后附上两种手写 Promise 的参考

http://www.lanhao.name/article/293

(未完)

5 回复

哈哈,你还是没有说清楚Promise到底是什么?有木有,继续加油等未完篇

@i5ting 放心吧,肯定会太监的

@captainblue2013 别呀,继续加油~~看好你呦~

@i5ting 主要还是吃力。如果企图用很规范很完整的语言去阐述Promise,网上其实很多了,而且出来的效果,让人觉得晦涩难懂,很抽象,有时天马行空。所以我想通过一些case,层层递进去弄明白Promise到底在做什么,经历完整个过程,大概不需要语言也能在心里形成一个对Promise的具象化的理解。

所以还是很吃力

@captainblue2013 玩玩这个http://bevacqua.github.io/promisees/,或许会有灵感

回到顶部