在很长的一段时间里我都不明白这个东西的作用,以及这个东西的核心,还有这个东西的原理。就是对此什么也不了解。但是大家好像又都用的热火朝天的。而且我在第一次升级答辩的时候还被问到了这个问题。自己随意一编造,居然还能蒙混过关。我的神呢,原来大家对此理解也不是很透彻。所以希望自己能够弄懂一点。听说这个也很重要。
主要参考的是JavaScript Promise迷你书
什么是Promise
Promise是抽象异步处理对象以及对其进行各种操作的组件。我的理解是可以将一些异步回调的操作规范化。
以下是一个简单Promise的代码。
var aaa = new Promise(function (resolve, reject) {
setTimeout(function () {
if(Math.random() > 0.5){
resolve('成功大于0.5');
}else{
reject('小于了0.5')
}
}, 1000);
}).then(function (value) {
console.log(value);
},function (value){
console.log(value);
})
- 首先我们 new 了一个 Promise 对象,接受两个参数,成功的回调和失败的回调。在 new 的一瞬间,里面的内容就已经执行了。
- 调用 then 可以将刚才执行的结果传入相应的两个 function 最终执行出结果来
Promise的状态
- “has-resolution” - Fulfilled:resolve (成功)时。此时会调用 onFulfilled
- “has-rejection” - Rejected:reject (失败)时。此时会调用 onRejected
- “unresolved” - Pending:既不是 resolve 也不是 reject 的状态。也就是 promise 对象刚被创建后的初始化状态等。其实当已经执行调用 then 的函数的时候,状态还是 pending,只有当 resolve 或者 reject 执行完成之后状态才会改变
Promise的异步调用
首先Promise只能使用异步调用方式,不要试图对异步回调函数进行同步调用。可能不会符合你的预期。下面我们看一下下面这个程序的执行步骤。这里用代码说明了为啥Promise.then是一个异步调用的。
// 首先执行
var promise = new Promise(function (resolve){
console.log("inner promise");
resolve(42);
});
// 异步执行
promise.then(function(value){
console.log(value);
});
// 同步执行
console.log("outer promise");
Promise的链式调用
这种方式适合只有一个Promise的时候,前一个会等后一个执行。后一个也能拿到前一个的return值。但是如果then中的function如果是异步的,后面的就拿不到return的数了,看执行结果能看到最后有什么区别
var promise = new Promise(function (resolve) {
resolve(100);
});
promise.then(function (value) {
return value * 2;
}).then(function (value) {
return value * 2;
}).then(function (value) {
setTimeout(function(){
console.log("1: " + value);
return value;
}, 1000)
}).then(function (value) {
console.log("2: " + value);
});
Promise和数组
上面已经遇到问题了,我们怎么去解决这个问题呢。如果then中全是异步的怎么办呢?写多个Promise来解决,个人觉得这个方法也是比较low。这样的话会挨着这行Promise,放在前面的先执行。
function getPromise(value, timer) {
return new Promise(function (resolve, reject) {
setTimeout(function(){
resolve(value);
}, timer)
});
}
var request = {
hello: function getComment() {
return getPromise('hello', Math.random() * 5000).then(function(value){
console.log(value);
});
},
world: function getPeople() {
return getPromise('world', Math.random() * 5000).then(function(value){
console.log(value);
});
}
};
request.hello().then(request.world);
上面这种重复调用then方法的方式如果增加到10个怎么办?这种方式写出来就不够简洁。那我们应该怎么做让上面这个方法变的简洁一点呢,下面这种写法后面
function getPromise(value, timer) {
return new Promise(function (resolve, reject) {
setTimeout(function(){
resolve(value);
}, timer)
});
}
function sayHello() {
return getPromise('hello', Math.random() * 5000).then(function(value){
console.log(value);
});
}
var request = [];
for(var i=0; i<10; i++){
request.push(sayHello);
}
var promise = Promise.resolve();
// 开始的地方
request.map(function(item, index, array){
promise = promise.then(item);
});
promise.then(function(){
console.log('all done')
})
Promise.all
上面这种方式是如果你有10个Promise,这10个Promise必须是下一个需要上一个执行完了之后再执行。后来需求变了,我希望把所有的Promise发出去,他们之间没有先后顺序,直到所有的Promise都执行完了给我一个回馈。这个怎么破呢?
下面这种Promise.all的方式就可以做到,数组中的请求是无需的,都是同时开始执行,根据自己的请求做出回馈调用resolve方法。数组中的所有的项都调用了resolve方法之后,then开始执行。
function getPromise(value, timer) {
return new Promise(function (resolve, reject) {
setTimeout(function(){
resolve(value);
}, timer)
});
}
var request = {
hello: function() {
return getPromise('hello', Math.random() * 5000).then(function(value){
console.log(value);
});
},
world: function() {
return getPromise('world', Math.random() * 5000).then(function(value){
console.log(value);
});
}
};
Promise.all([request.hello(), request.world()]).then(function(){
console.log('all done');
})
Promise.race
我又碰到了新的问题,如果我需要的是数组中的任意一个Promise完成我就要执行某个方法怎么办呢?
下面这种Promise.race就是无序的,只需要任意一种执行完毕之后就会触发后面的then
function getPromise(value, timer) {
return new Promise(function (resolve, reject) {
setTimeout(function(){
resolve(value);
}, timer)
});
}
var request = {
hello: function() {
return getPromise('hello', Math.random() * 5000).then(function(value){
console.log(value);
});
},
world: function() {
return getPromise('world', Math.random() * 5000).then(function(value){
console.log(value);
});
}
};
Promise.race([request.hello(), request.world()]).then(function(){
console.log('one done');
});
Promise的错误处理
可以直接在Promise中抛出一个错误,也可以直接在后面catch住错误,不管catch放在哪里,只要Promise中有错误跑出,都可以抓到错误并执行回调。
var promise = new Promise(function(resolve, reject){
throw new Error("message");
});
promise.catch(function(error){
console.error(error);
});
reject 和 throw
既然有了reject对于错误的处理,为什么还需要throw呢?首先上面说了,throw无论什么情况下都可以抓到。
借用迷你书的一个例子
function throwError(value) {
// 抛出异常
throw new Error(value);
}
// <1> onRejected不会被调用
function badMain(onRejected) {
return Promise.resolve(42).then(throwError, onRejected);
}
// <2> 有异常发生时onRejected会被调用
function goodMain(onRejected) {
return Promise.resolve(42).then(throwError).catch(onRejected);
}
// 运行示例
badMain(function(){
console.log("BAD");
});
goodMain(function(){
console.log("GOOD");
});
可以看到,console里只有GOOD,没有BAD。首先发现catch是有好处的。但是这也给我们带来了思考,当代码到达一定复杂度之后我们很难区分出throw出来的是我们主动抛出的错误还是浏览器抛出的异常。同样的,如果你希望报错之后就什么都不执行了当然就是选用reject比较好了。