Promise是一个对象,用来传递异步操作的消息。它代表了某个未来才会知道结果的事件(通常是一个异步操作),并且这个事件提供统一的 API,可供进一步处理。
有了 Promise 对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise 对象提供统一的接口,使得控制异步操作更加容易。
Promise是一个构造函数,Promise new出来的对象有reject、resolve、all、race等方法,原型Promise.prototype上有then、catch等方法。
1、Promise构造函数
Promise 构造函数接受一个函数作为参数,该函数的两个参数分别是 resolve 方法和 reject 方法。
如果异步操作成功,则用 resolve 方法将 Promise 对象的状态,从「未完成」变为「成功」(即从 pending 变为 resolved);
如果异步操作失败,则用 reject 方法将 Promise 对象的状态,从「未完成」变为「失败」(即从 pending 变为 rejected)。
var promise = new Promise(function(resolve, reject) {
if (/* 异步操作成功 */)
resolve(value);
else
reject(error);
});
2、Promise链式操作
从表面上看,Promise能够简化层层回调的写法,而实质上,Promise的精髓是“状态”,用维护状态、传递状态的方式来使得回调函数能够及时调用,它比传递callback函数要简单、灵活的多。
function async1(){
var promise = new Promise(function(resolve, reject){
//异步操作
setTimeout(function(){
console.log('First Finished!');
resolve('First data!');
}, 1000);
});
return promise;
}
function async2(){
var promise = new Promise(function(resolve, reject){
//异步操作
setTimeout(function(){
console.log('Second Finished!');
resolve('Second data!');
}, 2000);
});
return promise;
}
function async3(){
var promise = new Promise(function(resolve, reject){
//异步操作
setTimeout(function(){
console.log('Third Finished!');
resolve('Third data!');
}, 2000);
});
return promise;
}
async1()
.then(function(data){
console.log(data);
return async2();
})
.then(function(data){
console.log(data);
return async3();
})
.then(function(data){
console.log(data);
});
运行结果:
First Finished!
First data!
Second Finished!
Second data!
Third Finished!
Third data!
在then方法中,也可以直接return数据而不是Promise对象,在后面的then中就可以接收到数据了。
function async1(){
var promise = new Promise(function(resolve, reject){
//异步操作
setTimeout(function(){
console.log('First Finished!');
resolve('First data!');
}, 1000);
});
return promise;
}
function async2(){
var promise = new Promise(function(resolve, reject){
//异步操作
setTimeout(function(){
console.log('Second Finished!');
resolve('Second data!');
}, 2000);
});
return promise;
}
function async3(){
var promise = new Promise(function(resolve, reject){
//异步操作
setTimeout(function(){
console.log('Third Finished!');
resolve('Third data!');
}, 2000);
});
return promise;
}
async1()
.then(function(data){
console.log(data);
return async2();
})
.then(function(data){
console.log(data);
return 'Direct data!'; //直接返回数据
})
.then(function(data){
console.log(data);
});
运行结果:
First Finished!
First data!
Second Finished!
Second data!
Direct data!
3、resolve与reject方法
function createNumber(){
var promise = new Promise(function(resolve, reject){
//异步操作
setTimeout(function(){
var num = Math.ceil(Math.random()*10);
if (num <= 5)
resolve(num);
else
reject('Too big!');
}, 2000);
});
return promise;
}
createNumber()
.then(
function(data){
console.log('resolve');
console.log(data);
},
function(reason, data){
console.log('reject');
console.log(reason);
}
);
运行结果1:
resolve
1
运行结果2:
reject
Too big!
4、catch()方法
Promise对象除了then方法,还有一个catch方法,其实它和then的第二个参数一样,用来指定reject的回调,即then(function(data), function(err))等价于then(function(data)).catch(function(err))。
function createNumber(){
var promise = new Promise(function(resolve, reject){
//异步操作
setTimeout(function(){
var num = Math.ceil(Math.random()*10);
if (num <= 5)
resolve(num);
else
reject('Too big!');
}, 2000);
});
return promise;
}
createNumber()
.then(function(data){
console.log('resolve');
console.log(data);
})
.catch(function(reason){
console.log('reject');
console.log(reason);
});
运行结果1:
resolve
1
运行结果2:
reject
Too big!
catch方法还有另外一个作用:在执行resolve的回调时,如果抛出异常(代码出错),并不会报错卡死js,而是会进到这个catch方法中。
function createNumber(){
var promise = new Promise(function(resolve, reject){
// 异步操作
setTimeout(function(){
var num = Math.ceil(Math.random()*10);
if (num <= 5)
resolve(num);
else
reject('Too big!');
}, 2000);
});
return promise;
}
createNumber()
.then(function(data){
console.log('resolve');
console.log(data);
console.log(variety); // variety未定义
})
.catch(function(reason){
console.log('reject');
console.log(reason);
});
运行结果1:
resolve
1
reject
ReferenceError: variety is not defined
运行结果2:
rejected
Too big!
5、all()方法
Promise的all方法提供了并行执行异步操作的能力,并且在所有异步操作执行完后才执行回调。
all方法用于将多个异步操作(或Promise对象),包装成一个新的Promise对象。所有异步操作都完成,才会调用resolve方法指定的函数,只要其中一个异步操作失败,就会回调reject方法指定的函数。
all接收一个数组参数,里面的值最终都算返回Promise对象。
all会把所有异步操作的结果放进一个数组中传给then。
function async1(){
var promise = new Promise(function(resolve, reject){
//异步操作
setTimeout(function(){
console.log('First Finished!');
resolve('First data!');
}, 1000);
});
return promise;
}
function async2(){
var promise = new Promise(function(resolve, reject){
//异步操作
setTimeout(function(){
console.log('Second Finished!');
resolve('Second data!');
}, 2000);
});
return promise;
}
function async3(){
var promise = new Promise(function(resolve, reject){
//异步操作
setTimeout(function(){
console.log('Third Finished!');
resolve('Third data!');
}, 2000);
});
return promise;
}
Promise
.all([async1(), async2(), async3()])
.then(function(result){
console.log(result);
});
运行结果:
First Finished!
Second Finished!
Third Finished!
["First data!", "Second data!", "Third data!"]
6、race()方法
all方法的效果实际上是「谁跑的慢,以谁为准执行回调」,那么相对的就有另一个方法「谁跑的快,以谁为准执行回调」,这就是race方法。
function async1(){
var promise = new Promise(function(resolve, reject){
//异步操作
setTimeout(function(){
console.log('First Finished!');
resolve('First data!');
}, 1000);
});
return promise;
}
function async2(){
var promise = new Promise(function(resolve, reject){
//异步操作
setTimeout(function(){
console.log('Second Finished!');
resolve('Second data!');
}, 2000);
});
return promise;
}
function async3(){
var promise = new Promise(function(resolve, reject){
//异步操作
setTimeout(function(){
console.log('Third Finished!');
resolve('Third data!');
}, 2000);
});
return promise;
}
Promise
.race([async1(), async2(), async3()])
.then(function(result){
console.log(result);
});
运行结果:
First Finished!
First data!
Second Finished!
Third Finished!
可以用race给某个异步请求设置超时时间,并且在超时后执行相应的操作。
function getImg(){
var promise = new Promise(function(resolve, reject){
var img = new Image();
img.onload = function(){
resolve(img);
}
img.src = 'noExistImg.png';
});
return promise;
}
function timeout(){
var promise = new Promise(function(resolve, reject){
setTimeout(function(){
reject('Timeout');
}, 5000);
});
return promise;
}
Promise
.race([getImg(), timeout()])
.then(function(result){
console.log(result);
})
.catch(function(reason){
console.log(reason);
});
运行结果:
GET http://localhost/noExistImg.png 404 (Not Found)
Timeout
requestImg函数会异步请求一张图片,我把地址写为"xxxxxx",所以肯定是无法成功请求到的。timeout函数是一个延时5秒的异步操作。我们把这两个返回Promise对象的函数放进race,于是他俩就会赛跑,如果5秒之内图片请求成功了,那么遍进入then方法,执行正常的流程。如果5秒钟图片还未成功返回,那么timeout就跑赢了,则进入catch,报出“图片请求超时”的信息。