什么是Promise?
Promise是由 CommonJS 在 Promises/A 规范中提出来的,是js一部编程的重要概念,是比较就行的javascript一部变成解决方案之一。
常见的异步编程解决方案
- 回调函数
- 事件监听
- 发布/订阅
- Promise对象
在这里以上的及以上的集中解决方案就不一一说了,我们就拿一部函数简单说一下吧
1.在JQuery中我们通过异步请求拿到后台数据,都是以回调的方式。
$.get(url, (res => {
console.log(res);
})
2.如果我们需要发送多个请求,并且这些请求是有执行顺序的;那么我们就只能嵌套使用;如果有很多个嵌套呢?这就形成了“嵌套地狱
”·
$.get(url1, res1 => {
console.log(res1);
$.get(url2, res2 => {
console.log(res2);
$.get(url3, res3 => {
console.log(res3);
......
})
})
})
这样一来,在处理越多的异步逻辑时,就需要越深的回调嵌套;这个问题我们一会来解决
promises相关概念
1. then()方法介绍
根据 Promise/A 规范,promise
是一个对象,只需要then
这一个方法。then
方法带有如下三个参数:
- 成功回调
- 失败回调
- 前进回调(规范没有要求包括前进回调的实现)。
一个全新的 promise 对象从每个 then 的调用中返回。
2. Promise对象状态
Promise 对象代表一个异步操作,其不受外界影响,有三种状态:
- Pending:进行中、未完成的
- Resolved:已完成,又称 Fulfilled
- Rejected:已失败
1、创建出来的Promise
从未完成的状态开始,如果调用成功将进入已完成状态,如果失败将进入已失败状态。
2、当一个Promise
移动到已完成状态,那么所有注册到它的成功回调将被调用,而且会将成功结果传给它;另外,任何注册到promise
的成功回调,将会在它已经完成以后立即被调用。
3、当一个Promise
移动到已失败状态,那么调用的是失败调用;另外之后注册到Promise
的回调将不会被调用。
4、对包含前进特性的实现来说,Promise
在它离开未完成状态以前的任何时刻,都可以更新它的 progress。当progress被更新,所有的前进回调(progress callbacks)会被传递以progress的值,并被立即调用。前进回调被以不同于成功和失败回调的方式处理;如果你在一个 progress 更新已经发生以后注册了一个前进回调,新的前进回调只会在它被注册以后被已更新的 progress 调用。
5、注意:只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。
Promises的优势
1. 解决回调地狱问题
在我们上面说到的那个agax请求的```回调地狱``问题;代码如下:
$.get(url1, res1 => {
console.log(res1);
$.get(url2, res2 => {
console.log(res2);
$.get(url3, res3 => {
console.log(res3);
......
})
})
})
我们使用Promis解决的话,代码就会提高可读性;如下:
request(url) {
return new Promise((resolve, reject) => {
$.get(url, res => {
resolve(res);
})
});
}
request(url1).then(data => {
return request(url2);
}).then(data => {
return request(url3);
});
2. 更好的处理异常
比如下面代码我们使用 setTimeout 模拟异步操作,在其中抛出了个异常。但由于异步回调中,回调函数的执行栈与原函数分离开,导致外部无法抓住异常。
function testError(e) {
setTimeout(() => {
throw Error('请求失败')
}, 2000)
}
try {
testError(() => {
console.log('请求处理'); // 永远不会执行
})
} catch (error) {
console.log('触发异常', error); // 永远不会执行
}
使用Promises
,通过reject
方法把Promise````的状态置为
rejected,这样我们在
then```中就能捕捉到,然后执行Error情况的回调
function testError(callback) {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject('请求失败');
}, 2000)
})
}
testError().then(function(data){
console.log('请求处理');
console.log(data);
},function(reason, data){
console.log('触发异常');
console.log(reason);
});
当然我们在catch
方法中处理reject
回调;如下:
testError().then(data => {
console.log('请求处理');
console.log(data);
}).catch(reason => {
console.log('触发异常');
console.log(reason);
});
Promises方法详解
1. then()方法
简单来说,then()
方法就是把原来的方法分离出来,在异步执方法调用完成以后,然后链式的用方法执行回调函数。
Promise
的优势就在于这个链式调用。我们可以在then()
方法中继续返回Promise
对象,然后继续调用后面的then()
方法。
而 Promise 的优势就在于这个链式调用。我们可以在 then 方法中继续写 Promise 对象并返回,然后继续调用 then 来进行回调操作。
让我们来开一下实例,这里我们使用setTimeout
来模仿异步操作:
function f1() {
console.log("第一个方法开始执行!!!");
return new Promise(function(resolve, reject){// 第一个异步操作
setTimeout(function() {
console.log("第一个回调执行完成!!!");
resolve("data1");
}, 1000);
});
}
function f2(data) {
console.log(`第二个方法开始执行:方法一的执行结果为***${data}***!!!`);
return new Promise(function(resolve, reject){// 第二个异步操作
setTimeout(function() {
console.log("第二个个回调执行完成!!!");
resolve("data2");
}, 1000);
});
}
function f3(data) {
console.log(`第三个方法开始执行:方法二的执行结果为***${data}***!!!`);
return new Promise(function(resolve, reject){// 第三个异步操作
setTimeout(function() {
console.log("第三个个回调执行完成!!!");
resolve("data3");
}, 1000);
});
}
f1().then(data => {
return f2(data);
}).then(data => {
return f3(data);
}).then(data => {
console.log(data);
});
当然我们的代码可以更简单,如下:
f1().then(f2).then(f3).then(data => {
console.log(data);
});
执行结果如下:
第一个方法开始执行!!!
第一个回调执行完成!!!
第二个方法开始执行:方法一的执行结果为***data1***!!!
第二个个回调执行完成!!!
第三个方法开始执行:方法二的执行结果为***data2***!!!
第三个个回调执行完成!!!
data3
2. reject()方法
上面样例我们通过resolve
方法把Promise
的状态置为完成态Resolved
,这时then
方法就能捕捉到变化,并执行“成功
”情况的回调。
而reject
方法就是把Promise
的状态置为已失败Rejected
,这时then
方法执行“失败
”情况的回调。
我们修改一下上面的例子,在线执行一次:
function f1() {
console.log("第一个方法开始执行!!!");
return new Promise(function(resolve, reject){// 第一个异步操作
setTimeout(function() {
console.log("第一个回调执行完成!!!");
reject("调用失败");
}, 1000);
});
}
function f2(data) {
console.log(`第二个方法开始执行:方法一的执行结果为***${data}***!!!`);
return new Promise(function(resolve, reject){// 第二个异步操作
setTimeout(function() {
console.log("第二个个回调执行完成!!!");
resolve("data2");
}, 1000);
});
}
f1().then(f2, data => {
console.log(data);
});
执行结果如下:
第一个方法开始执行!!!
第一个回调执行完成!!!
调用失败
如果你决定只处理异常,那不妨把then()
的第一个参数设置为null
3. catch()方法
其实它和then()
的第二个方法类似,用来指定reject()
的回调,但是它还有另外一个用处,如下演示:
function f1() {
console.log("第一个方法开始执行!!!");
return new Promise(function(resolve, reject){// 第一个异步操作
setTimeout(function() {
console.log("第一个回调执行完成!!!");
reject("调用失败");
}, 1000);
});
}
function f2(data) {
console.log(`第二个方法开始执行:方法一的执行结果为***${data}***!!!`);
return new Promise(function(resolve, reject){// 第二个异步操作
setTimeout(function() {
console.log("第二个个回调执行完成!!!");
resolve("data2");
}, 1000);
});
}
f1().then(data => {
f2(data);
}).catch(err => {
console.log(err);
});
上面的例子,f2()
方法并没有执行;这也就是catch()
的另一个用处,在链式调用then()
方法的时候,如果中间有一个出错了,那么他不会卡死,而是忽略后面的then()
方法,直接调用catch()
方法进行异常处理。
我们还可以添加多个catch()
,进行精确的步骤操作:
test().then(function() {
return a();
}).catch(TypeError, function(e) {
// 如果定义了a,将在此处结束,因为
}).catch(ReferenceError, function(e) {
// 如果完全没有定义a,将在此处结束
}).catch(function(e) {
// 如果上面都没有捕捉到错误,将在这里捕捉
});
4. all()方法
Promise
的all()
方法提供了并行执行异步操作的能力,并且在所有异步操作执行完后才执行回调。
例如:
function f1() {
console.log("第一个方法开始执行!!!");
return new Promise(function(resolve, reject){// 第一个异步操作
setTimeout(function() {
console.log("第一个回调执行完成!!!");
resolve("data1");
}, 1000);
});
}
function f2() {
console.log(`第二个方法开始执行!!!`);
return new Promise(function(resolve, reject){// 第二个异步操作
setTimeout(function() {
console.log("第二个个回调执行完成!!!");
resolve("data2");
}, 1000);
});
}
Promise.all([f1(), f2()]).then(data => {
console.log(data);
});
执行结果:
第一个方法开始执行!!!
第二个方法开始执行!!!
第一个回调执行完成!!!
第二个个回调执行完成!!!
> ["data1", "data2"]
5. race()方法
race
按字面解释,就是赛跑的意思。race()
的用法与all()
一样,只不过all()
是等所有异步操作都执行完毕后才执行then()
回调。而race()
的话只要有一个异步操作执行完毕,就立刻执行then()
回调。
function f1() {
console.log("第一个方法开始执行!!!");
return new Promise(function(resolve, reject){// 第一个异步操作
setTimeout(function() {
console.log("第一个回调执行完成!!!");
resolve("data1");
}, 1000);
});
}
function f2() {
console.log(`第二个方法开始执行!!!`);
return new Promise(function(resolve, reject){// 第二个异步操作
setTimeout(function() {
console.log("第二个个回调执行完成!!!");
resolve("data2");
}, 1000);
});
}
Promise.race([f1(), f2()]).then(data => {
console.log(data);
});
执行结果:
第一个方法开始执行!!!
第二个方法开始执行!!!
第一个回调执行完成!!!
data1
第二个个回调执行完成!!!
注意:其它没有执行完毕的异步操作仍然会继续执行,而不是停止。
参考:
https://www.cnblogs.com/sweeeper/p/8442613.html
http://www.hangge.com/blog/cache/detail_1639.html