Promise对象的基本用法

主要作用

1.用来传递异步操作的消息
2.三种状态:pending、Resolved、Rejected,而且只能从第一种状态转到后两者状态之一。
3.缺点
(1)一旦新建就会立即执行
(2)如果不设置回调函数,Promise内部抛出的错误不会反应到外部
(3)如果处于Pending状态,不知道目前到底进行到哪一个阶段。
4.基本用法

var promise = new Promise(function(resolve,reject){
	//...
	if(/*异步操作成功*/){
		resolve(value);//这个函数是js引擎提供的,不用自己部署,这个value可以传递给then方法的参数
	}else{
		reject(error);//这个函数是js引擎提供的,不用自己部署,这个value可以传递给then方法的参数
	}else{
	}
})

接收一个函数作为参数,而这个函数的两个参数也是两个函数,是由js引擎提供,不用自己部署。后一个函数不一定要提供
resolve函数的作用是将pending转为resolved状态,在异步操作成功的时候调用,reject则是将其转为rejected状态,在异步操作失败的时候调用。

then()方法

1.作用:当Promise实例生成以后,可以用then方法分别指定Resolved状态和Rejected状态的回调函数。定义在原型对象Promise.prototype上的,为Promise实例添加状态改变时的回调函数
2.then方法返回的是一个新的Promise实例,因此可以采用链式写法,即then方法后面还可以调用另一个then方法。

//异步加载图片
function loadImageAsync(url){
	return new Promise(function(resolve,reject){
		var image = new Image();
		image.onload = function(){
			resolve(image);
		};
		image.onerror = function(){
			reject(new Error('cound not load image at'+url));
		};
		image.src = url;
	})
}
//ajax操作
var getJSON = function(url){
	var promise = new Promise(function(resolve,reject){
		var client = new XMLHttpRequest();
		client.open("GET",url);
		client.onreadystatechange = handler;
		client.responseType = "json";
		clent.setRequestHeader("Accept","application/json");
		client.send();

		function handler(){
			if(this.readyState !== 4){
				return;
			}
			if(this.status === 200){
				resolve(this.response);//当转为resolved状态的时候,调用then方法
				//就会把this.response这个值传递给then方法参数中的value。
			}else{
				reject(new Error(this.statusText));
				//当promise的状态转为rejected时,就会把这个错误信息传递给then方法
			}
		};
	});
	return promise;
}

getJSON("/post.json").then(function(json)){
	console.log(json);//这里的json即是上面的this.response
},function(error){
	console.log(error);
}

getJSON("/post.json").then(function(json){
	return json.post; //这里返回的可能还是一个Promise对象
}).then(function(post)){
	//上一个then方法的返回值传递给这个then方法的函数的参数
});

Promise.prototype.catch()

1.这个方法是指定发生错误时的回调函数
注意:如果Promise的状态已经是Resolved,再抛出错误也是无效的。

var promise = new Promise(function(resolve,reject){
	resolve("ok");//因为这里已经是resolved状态了,下面再抛出错误也无效
	throw new Error('test');
})

promise.then(function(value){
	console.log(value);
}).catch(function(error){
	console.log(error);
})
//ok这里只会打印ok

还有要注意的是:

(1)Promise对象的错误具有“冒泡”性质,一直会向后面传递,直到捕获为止。不过一般来说,不要在then方法中定义Rejected状态的回调函数,最好在外面用catch方法
(2)还有和传统的try...catch块不同,如果没有使用catch方法来指定错误处理的回调函数,Promise对象抛出的错误不会传递到外层代码,即不会有任何反应。
(3)如果在Promise内部,状态已经是resolved了,再抛出错误,这个错误也就是在Promise函数体外抛出的。原因是此时Promise的函数体已经运行结束。
(4)catch方法返回的还是一个Promise对象,因此后面还可以紧接着调用then方法。

有两种创建Promise对象实例以及调用它的方法

//第一种,直接利用new命令,创建一个Promise实例

var promise = new Promise(function(resolve,reject){
	//...
})
promise.then(function(value){
	//...
})

//第二种,用一个函数,然后在其中用return返回一个Promise实例,

var someAsyncThing = function(){
	return new Promise(function(resolve,reject){
		//下面一行会报错
		resolve(x+2);
	})
}
someAsyncThing().catch(function(error){
	console.log('oh no',error);
}).then(function(){
	console.log('carry on');
});
//oh no [ReferenceError: x is not defined]
//carry on

创建Promise实例和then方法和catch方法之间的关系

这里只是我的个人理解:
1.当创建了Promise实例的时候,就会在其中调用两个函数:resolve()和reject(),当resolve()函数成功执行的时候,Promise对象的状态就变成了Resolved,此时就调用then方法,同样的,reject()函数成功执行的时候,Promise对象的状态就变成了Rejected此时也可以调用then方法,但是处理错误的时候,一般推荐使用catch方法。
2.当在创建实例的过程中,会调用resolve()方法,如果这个方法的后面还有throw命令或者抛出错误,此时得判断这个错误是由Promise函数体内部还是外部抛出的,如果是内部抛出,有无catch都无所谓,如果是外部错误,没有catch的话,就会报错(Uncaught Error)。
3.还有在创建实例的过程中,resolve方法的参数,如果是值,就会传递给then方法的函数的参数value。如果还是一个Promise对象,就会返回另一个异步操作。

还有几个小的还很实际的方法

1.Promise.all():用于将多个Promise实例包装成一个新的Promise实例。
参数:可以接受一个数组,但是这个数组的所有成员都是Promise对象的实例,如果不是,就会先调用Promise.resolve方法,将参数转为Promise实例。
也不一定是数组,但必须具有Iterator接口,且返回的每个成员都是Promise实例
2.Promise.race():同样是将多个Promise实例包装成一个新的Promise实例。
var p = Promise.race([p1,p2,p3]);
上面的代码中,只要p1,p2,p3中有一个实例率先改变,p的状态就跟着改变。那个率先改变的Promise实例的返回值,就会传递给p的回调函数。同样如果不是Promise实例,就会调用resolve方法来转化为实例。

var p = Promise.race([
	fetch('/resource-that-may-a-while'),new Promise(function(resolve,reject){
		setTimeout(()=>reject(new Error('request timeout')),5000)
	})
])
p.then(response => console.log(response));
p.catch(error => console.log(error));
//上面的代码,如果5秒之内fetch方法无法返回结果,变量p的状态就会变成Rejected,从而触发 catch方法指定的回调函数

3.Promise.resolve()
这个方法就是得到一个Promise对象。
参数:可以是值,也可以是具有then方法的对象
(1)如果是值,如字符串,则返回一个新的Promise对象,那么对象的状态变为resolved,而且参数也会同时传递给回调函数。
(2)其实也可以允许调用的时候,不带参数
4.Promise.reject():也会返回一个新的Promise实例,状态为Rejected。而且reason这个参数会被传递给实例的回调函数。

两个有用的附加方法

ES6的Promise API提供的方法不是很多,可以自己部署一些有用的方法,下面部署两个不在ES6中但很有用的方法。
1.done():这个是为了避免Promise对象的回调链,不管以then方法还是catch方法结尾,要是最后一个方法抛出错误,都有可能无法捕捉到(因为Promise内部的错误冒泡到全局)。
所以,我们提供一个done方法,总是处于回调链的尾端,保证抛出任何可能出现的错误。

asyncFunc()
	.then(f1)
	.catch(r1)
	.then(f2)
	.done();
Promise.prototype.done = function(onFulfilled,onRejected){
	this.then(onFulfilled,onRejected)
		.catch(function(reason){
			//抛出一个全局错误
			setTimeout(() => {throw reason},0);
		});
}

由上面可以看出,done方法可以像then方法那样使用,也可以不提供任何参数,但是不管怎么样,done方法都会捕捉到任何可能出现的错误,并向全局抛出。

2.finally()方法:用于指定不管Promise对象最后状态如何都会执行的操作。它和done方法的最大区别在于,它接受一个普通的回调函数作为参数,该函数不管怎么样都必须执行
下面是一个例子:服务器使用Promise处理请求,然后使用finally方法关掉服务器

server.listen(0)
	.then(function(){
		//run test
	})
	.finally(server.stop);
Promise.prototype.finally = function(callback){
	let P = this.constructor;
	return this.then(
		value => P.resolve(callback()).then(() => value),//调用resolve函数之后返回一个Promise对象,然后再调用then方法
		reason => P.resolve(callback()).then(()=> {throw reason})
	)
}//㐀Promis对象的 状态如何,都会执行回调函数callback

应用

1.加载图片:可以将图片的加载写成一个Promise,一旦加载完成,Promise的状态就发生变化。

const preloadImage = function(path){
	return new Promise(function(resolve,reject){
		var image = new Image();
		image.onload = resolve;
		image.onerror = reject;
		image.src = path;
	})
}

2.Generator函数和Promise的结合
使用Generator函数管理流程,遇到异步操作,通常返回一个Promise对象

//getFoo函数,返回一个Promise对象
function getFoo(){
	return new Promise(function(resolve,reject){
		resolve('foo');
	})
}


var g = function* (){
	try{
		var foo = yield getFoo(); //执行run(g)的时候,这里会返回一个Promise对象,getFoo()是异步操作
		console.log(foo);
	}catch(e){
		console.log(e);
	}
};
//总结一下,调用Generator函数,就会返回一个遍历器对象,代表Generator函数的内部指针,以后每次调用遍历器对象的next方法,就会返回一个包括value和done属性的对象。value的值是yield语句后面那个表达式的值;done属性是一个布尔值。
//通过run方法来包装异步函数
function run(generator){
	var it = generator();
	function go(result){
		if(result.done) return result.value;//这里的result是getFoo函数的返回值,也就是一个Promise对象
		return result.value.then(function(value){
			return go(it.next(value));//这里不用go执行,直接执行it.next(value)也可以,为什么这里要用go???
		},function(error){
			return go(it.throw(error));
		})
	}
	go(it.next());//执行it.next()之后就会返回一个包含value和done属性的对象
}
run(g);
posted @ 2017-08-03 17:58  sminocence  阅读(536)  评论(0编辑  收藏  举报