es6 javascript的Promise对象(下)

5 Promise.all()

Promise.all方法用于将多个 Promise 实例, 包装成一个新的 Promise 实例。
var p = Promise.all([p1, p2, p3]);
上面代码中, Promise.all方法接受一个数组作为参数, p1、 p2、 p3都是 Promise 对象的实例, 如果不是, 就会先调用下面讲到的Promise.resolve方法, 将参数转为 Promise 实例, 再进一步处理。( Promise.all方法的参数可以不是数组, 但必须具有 Iterator 接口, 且返回的每个成员都是 Promise 实例。)
p的状态由p1、 p2、 p3决定, 分成两种情况。
( 1) 只有p1、 p2、 p3的状态都变成fulfilled, p的状态才会变成fulfilled, 此时p1、 p2、 p3的返回值组成一个数组, 传递给p的回调函数。
( 2) 只要p1、 p2、 p3之中有一个被rejected, p的状态就变成rejected, 此时第一个被reject的实例的返回值, 会传递给p的回调函数。
下面是一个具体的例子。

//  生成一个 Promise 对象的数组
var promises = [2, 3, 5, 7, 11, 13].map(function(id) {
	return getJSON("/post/" + id + ".json");
});
Promise.all(promises).then(function(posts) {
	// ...
}).catch(function(reason) {
	// ...
});
上面代码中, promises是包含 6 个 Promise 实例的数组, 只有这 6 个实例的状态都变成fulfilled, 或者其中有一个变为rejected, 才会调用Promise.all方法后面的回调函数。
下面是另一个例子。

const databasePromise = connectDatabase();
const booksPromise = databaseProimse
	.then(findAllBooks);
const userPromise = databasePromise
	.then(getCurrentUser);
Promise.all([
		booksPromise,
		userPromise
	])
	.then(([books, user]) => pickTopRecommentations(books, user));
上面代码中, booksPromise和userPromise是两个异步操作, 只有等到它们的结果都返回了, 才会触发pickTopRecommentations这个回调函数。

6 Promise.race()

Promise.race方法同样是将多个 Promise 实例, 包装成一个新的 Promise 实例。

var p = Promise.race([p1, p2, p3]);
上面代码中, 只要p1、 p2、 p3之中有一个实例率先改变状态, p的状态就跟着改变。 那个率先改变的 Promise 实例的返回值, 就传递给p的回调函数。
Promise.race方法的参数与Promise.all方法一样, 如果不是 Promise 实例, 就会先调用下面讲到的Promise.resolve方法, 将参数转为 Promise 实例,再进一步处理。
下面是一个例子, 如果指定时间内没有获得结果, 就将 Promise 的状态变为reject, 否则变为resolve。

var p = Promise.race([
	fetch('/resource-that-may-take-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方法指定的回调函数。


7 Promise.resolve()

有时需要将现有对象转为 Promise 对象, Promise.resolve方法就起到这个作用。

var jsPromise = Promise.resolve($.ajax('/whatever.json'));
上面代码将 jQuery 生成的deferred对象, 转为一个新的 Promise 对象。
Promise.resolve等价于下面的写法。

Promise.resolve('foo')
	//  等价于
new Promise(resolve => resolve('foo'))
Promise.resolve方法的参数分成四种情况。
( 1) 参数是一个 Promise 实例如果参数是 Promise 实例, 那么Promise.resolve将不做任何修改、 原封不动地返回这个实例。
( 2) 参数是一个thenable 对象
thenable对象指的是具有then方法的对象, 比如下面这个对象。

let thenable = {
	then: function(resolve, reject) {
		resolve(42);
	}
};
Promise.resolve方法会将这个对象转为 Promise 对象, 然后就立即执行thenable对象的then方法。

let thenable = {
	then: function(resolve, reject) {
		resolve(42);
	}
};
let p1 = Promise.resolve(thenable);
p1.then(function(value) {
	console.log(value); // 42
});
上面代码中, thenable对象的then方法执行后, 对象p1的状态就变为resolved, 从而立即执行最后那个then方法指定的回调函数, 输出 42。
( 3) 参数不是具有then 方法的对象, 或根本就不是对象
如果参数是一个原始值, 或者是一个不具有then方法的对象, 则Promise.resolve方法返回一个新的 Promise 对象, 状态为Resolved。

var p = Promise.resolve('Hello');
p.then(function(s) {
	console.log(s)
});
// Hello
上面代码生成一个新的 Promise 对象的实例p。 由于字符串Hello不属于异步操作( 判断方法是它不是具有 then 方法的对象), 返回 Promise 实例的状态从一生成就是Resolved, 所以回调函数会立即执行。 Promise.resolve方法的参数, 会同时传给回调函数。
( 4) 不带有任何参数
Promise.resolve方法允许调用时不带参数, 直接返回一个Resolved状态的 Promise 对象。
所以, 如果希望得到一个 Promise 对象, 比较方便的方法就是直接调用Promise.resolve方法。

var p = Promise.resolve();
p.then(function() {
	// ...
});
上面代码的变量p就是一个 Promise 对象。
需要注意的是, 立即resolve的 Promise 对象, 是在本轮“ 事件循环”( event loop) 的结束时, 而不是在下一轮“ 事件循环” 的开始时。

setTimeout(function() {
	console.log('three');
}, 0);
Promise.resolve().then(function() {
	console.log('two');
});
console.log('one');
// one
// two
// three
上面代码中, setTimeout(fn, 0) 在下一轮“ 事件循环” 开始时执行, Promise.resolve() 在本轮“ 事件循环” 结束时执行, console.log(’one‘) 则是立即执行, 因此最先输出。


8 Promise.reject()

Promise.reject(reason) 方法也会返回一个新的 Promise 实例, 该实例的状态为rejected。 它的参数用法与Promise.resolve方法完全一致。

var p = Promise.reject(' 出错了 ');
//  等同于
var p = new Promise((resolve, reject) => reject(' 出错了 '))
p.then(null, function(s) {
	console.log(s)
});
//  出错了
上面代码生成一个 Promise 对象的实例p, 状态为rejected, 回调函数会立即执行。


9 两个有用的附加方法

ES6 的 Promise API 提供的方法不是很多, 有些有用的方法可以自己部署。 下面介绍如何部署两个不在 ES6 之中、 但很有用的方法。

9.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方法那样用, 提供Fulfilled和Rejected状态的回调函数, 也可以不提供任何参数。 但不管怎样, done都会捕捉到任何可能出现的错误, 并向全局抛出。


9.2 finally()

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),
		reason => P.resolve(callback()).then(() => {
			throw reason
		})
	);
};
上面代码中, 不管前面的 Promise 是fulfilled还是rejected, 都会执行回调函数callback。


10 应用

10.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;
	});
};


10.2 Generator 函数与 Promise 的结合

使用 Generator 函数管理流程, 遇到异步操作的时候, 通常返回一个Promise对象。

function getFoo() {
	return new Promise(function(resolve, reject) {
		resolve('foo');
	});
}
var g = function*() {
	try {
		var foo = yield getFoo();
		console.log(foo);
	} catch(e) {
		console.log(e);
	}
};

function run(generator) {
	var it = generator();

	function go(result) {
		if(result.done) return result.value;
		return result.value.then(function(value) {
			return go(it.next(value));
		}, function(error) {
			return go(it.throw(error));
		});
	}
	go(it.next());
}
run(g);
上面代码的 Generator 函数g之中, 有一个异步操作getFoo, 它返回的就是一个Promise对象。 函数run用来处理这个Promise对象, 并调用下一个next方法。



  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值