- promise是为了更好的管理异步操作的一种技术,有了它可以让异步请求代码更已读,更易写
为什么要用Promise
- 很多请求必须依赖上一个请求的数据,在没有promise的情况下,我们必须在上一个请求的回调函数的成功回调中,开始新的请求,依次往下,必须层层嵌套,像洋葱包裹。这让代码显得很乱。比如下面:
$.ajax({
type:"get",
url:"data/all_goods.json",
async:true,
success:function(data){
//进行ajax的嵌套
$.ajax({
type:"get",
url:"data/friut_goods.json",
async:true,
success:function(data){
.......
}
});
}
});
如何理解Promise
- Promise的三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)
- resolved(已定型)包括 fulfilled 和 rejected,但是一般情况下指的是成功
//我们可以先写一个 普通的函数test,生成一个随机数,大于1调用resolve。小于零调用reject
function test(resolve, reject) {
var timeOut = Math.random() * 2;
console.log('set timeout to: ' + timeOut + ' seconds.');
setTimeout(function() {
if (timeOut < 1) {
console.log('call resolve()...');
resolve('200 OK');
} else {
console.log('call reject()...');
reject('timeout in ' + timeOut + ' seconds.');
}
}, timeOut * 1000);
}
//新建一个promise对象,并传入回调函数
var p = new Promise(test)
p.then(function(date) {
console.log(date)
}).catch(function(date) {
console.log(date)
})
多异步操作的链式调用
- 当我们只new一个promise对象的时候,我们没有调用,我们传进去的函(setTimeout)数就执行了,所以我们通常把它放到一个函数中,需要的时候再调用
- 为了链式调用我们这样,在每次then的回调函数中,我们return 一个新的promise任务,这样就可以继续.then()下去
//模拟下载数据
function requestURL(url){
//创建promise resolve解决 reject拒绝
var promise = new Promise(function(resolve,reject){
var b = true;//模拟请求是否成功
// if(url == "http://getUserList"){//模拟请求http://getUserList时发生错误
// b = false;
// }
setTimeout(function(){
if(b == true){//如果请求成功
resolve(url + "-成功");
}else{//请求失败
reject(url + "-失败");
}
},1000);
});
console.log(promise);
return promise;
}
//step1:调用requestURL函数 - 返回promise对象
//step2:通过.then传入一个匿名函数,成功回调
//step3:通过.catch传入匿名函数,失败回调
requestURL("http://login").then(function(data){
console.log("data = "+data);
return requestURL("http://getUserList");//返回getUserList请求的promise
}).then(function(data){
console.log("data = "+data);
return requestURL("http://sendMessage");//返回sendMessage请求的promise
}).then(function(data){
console.log("data = "+data);
console.log("全部的请求成功!");
}).catch(function(error){
console.log("发生错误 error = " + error);
});
all race用法
- all用法,无论顺序如何当所有的异步过程都完成后执行.then的回调函数
- race用法,当所有的其中一个执行完毕就调用then()的回调函数
//all用法,无论顺序如何当所有的异步过程都完成后执行.then的回调函数
Promise.all([runAsync1(),runAsync2(),runAsync3()]).then(function(){//注意all里面的数据是数组
console.log("三个异步操作都完成");
})
//race用法,当所有的其中一个执行完毕就调用then()的回调函数
Promise.race([runAsync1(),runAsync2(),runAsync3()]).then(function(){//注意all里面的数据是数组
console.log("最快的异步操作完成");
})
function runAsync1() {
var p = new Promise(function(resolve, reject) {
//做一些异步操作
setTimeout(function() {
console.log('异步任务1执行完成');
resolve('随便什么数据1');
}, 1000);
});
return p;
}
function runAsync2() {
var p = new Promise(function(resolve, reject) {
//做一些异步操作
setTimeout(function() {
console.log('异步任务2执行完成');
resolve('随便什么数据2');
}, 2000);
});
return p;
}
function runAsync3() {
var p = new Promise(function(resolve, reject) {
//做一些异步操作
setTimeout(function() {
console.log('异步任务3执行完成');
resolve('随便什么数据3');
}, 2000);
});
return p;
}
一个应用:让代码延迟一秒
错误的写法
让接下来的代码延迟一秒再运行,我见过这么写的。。。
setTimeout(function(){
console.log('延迟一秒');
}, 1000);
// 。。。想要延迟执行的代码
console.log('一秒之后');
显然这大错特错,js的异步代码总是在同步代码运行结束之后执行,所以想要延迟执行的代码反而先执行了
正确的写法
setTimeout(function(){
// 。。。想要延迟执行的代码
console.log('一秒之后');
}, 1000);
显然如果逻辑不是很复杂这已经足够了
用Promise封装
正常看来,这样麻烦的封装一个代码延迟不免有杀鸡用牛刀的感觉。但是们要学习promise,而setTimeout是最基本的异步操作,我们就用promise封装一下 因为正常情况下setTimeout只有成功没有失败,简单起见只写成功
var oneSec = new Promise(function(resolve){
setTimeout(function(){
console.log('setTimeout 在promise内执行完成');
resolve('timeOut OK');
}, 1000)
})
动图我们可以发现,我们new一个promise的过程中,promise中的异步代码已经执行
。
而且一开始打印的oneSec是undefined,执行完成之后是一个resolve状态的promise
接下来我们用then接受,在then的回调里写延迟的代码。
var oneSec = new Promise(function(resolve){
setTimeout(function(){
console.log('setTimeout 在promise内执行完成');
resolve('timeOut OK');
}, 2000)
});
oneSec.then(() => {
console.log('成功延迟')
})
最后我们用async await 进行延迟
var oneSec = new Promise(function(resolve){
setTimeout(function(){
console.log('1.setTimeout 在promise内执行完成');
resolve('3.timeOut OK');
}, 2000)
});
async function f(){
var res = await oneSec;
console.log("2.两秒以后");
console.log(res);
}
f();
我们可以看到 await 返回的值,是resolve() 传出的,相同的then()回调函数参数也是这个数据。