1、Promise的介绍
-
promise是异步操作的一种解决方案
-
异步的概念:
- ----异步(Asynchronous, async)和同步(Synchronous, sync);
- ----异步按照代码的顺序执行,异步不按照代码的顺序执行,异步的执行效果更佳;
-
什么时候使用异步编程
-
----在前端编程中(后端也一样),我们在处理一些简短、快速的操作时,例如计算1+1的结果,往往在主线程中就可以完成。主线程作为一个线程,不能够同时接受多方面的请求。所以,当一个事件没有结果时,界面将无法处理其他请求;
-
----现在有一个按钮,如果我们设置它的onclick事件为一个死循环,那么,当这个按钮按下的时候,整个网页将会失去响应;
-
----为了避免这种情况发生,我们常常用子线程来完成一些可能消耗时间足够长以至于被用户察觉的事情(一些需要等待某个时机在背后自动执行任务,比如:事件监听),比如读取一个大文件或者发出一个网络请求。因为子线程独立于主线程,所以即使出现阻塞也不会影响到主线程的运行。但是子线程有一个局限:一旦发射以后就会与主线程失去同步,我们无法确立他的结束,如果结束之后需要处理一些事情,比如处理来自服务器的信息,我们是无法将它合并到主线程中的。
-
JavaScript是单线程语言,为了解决多线程问题,JavaScript中的异步操作函数往往通过回调函数来实现异步任务的结束处理;
-
-
-
回调函数
-
在JavaScript中,回调函数具体的定义为:函数A作为参数传递到另一个函数B中
-
-
setTimeout(() => { console.log('one'); }, 3000) console.log('two');
-
setInterval()和setTimeout()是两种异步语句;
-
异步(asynchronous):不会阻塞CPU继续执行其他语句,当异步完成时(包含回调函数的主函数额正常语句完成时),会执行"回调函数"(callback);
-
-
2、异步任务案例
-
function fn() { document.querySelector('#demo').innerHTML = 'fn函数'; } setTimeout(fn, 3000);
-
setTimeout就是一个消耗了3秒的执行过程, 它的第一个参数是一个回调函数, 第二个参数是延迟时间, 这个函数执行之后, 会产生一个子线程,子线程会等待3秒,然后执行回调函数fn,在页面中输出:fn函数;
-
-
setTimeout(() => { document.querySelector('#demo').innerHTML = 'fn函数'; }, 3000)
3、setTimeout案例
-
setTimeout(() => { console.log('xx省'); setTimeout(() => { console.log('xx市'); setTimeout(() => { console.log('xx县'); }, 1000) }, 1000) }, 1000)
4、Promise的含义
-
Promise是异步编程的一种解决方案,比传统的解决方案--回调函数和事件--更合理、更强大。它由社区最再提出和实现,ES6将其写进了语言标准,统一了用法,原生提供了Parmise对象;
-
所谓Promise,简单来说就是一个容器,里面保存着某个未来才会结束的事件(通常是一种异步操作)的结果。从语法上说Promise是一个对象,从它可以获取异步操作的消息。Promise提供了统一的API,各种异步操作都可以用同样的方法进行处理;
-
Promise有三个状态:pending(等待)、fulfilled或resolved(成功)、rejected(失败);
-
并且Promise必须接收一个回调函数,这个回调函数有两个参数,这两个参数也是两个函数,(resolve,rejest) => {};
-
实例化Promise后,默认是等待状态;
-
当执行resolve()函数时,Promise从等待状态 --- 成功状态;
-
当执行reject()函数时,Promise从等待状态 --- 失败状态;
-
注意:当Promise的状态一旦从等待状态转变为某一个状态,后续的转变就自动忽略了,比如:先调用resolve()在调用reject(),那么Promise的最终状态是成功状态;
-
-
有了Promise对象,就可以将异步操作已同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise对象,提供了统一的接口,是的控制异步操作更加容易;
-
Promise也有一些缺点,首先,无法取消Promise,一但新建它就会立即执行,中途不能取消。其次,如果不设置回调函数,Promise内部抛出的错误,不会反映到外部。最后,当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即可完成);
-
ES5规定,Promise对象是一个构造函数,用来生成Promise实例;
-
Promise构造函数接受一个函数为参数,该函数的两个参数分别是resolve()和reject(),他们是两个函数,有JavaScript引擎提供,不用自己部署;
-
resolve函数的作用是,将Promise对象的状态从”未完成“变成”成功“(即从pending变成resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
-
reject函数的作用是,将Promise对象的状态从”未完成“变为”失败“(即从pending变成rejected),在异步操作报出的错误,作为参数传递出去;
-
let p = new Promise(function(a, b) { console.log(a); console.log(b); }) console.log(p);
-
-
Promise实力生成后,可以使用then方法分别指定resolved状态和rejected状态的回调函数;
-
resolve和reject函数时可以接受参数的:
-
resolve()接收的参数会传递给then方法的第一个回调函数;
-
reject()接收的参数会传递给then方法的第二个回调函数;
-
通常我们不仅仅会传递一个基本数据类型的值,还常常传递对象,比如再reject中传递一个错误对象;
-
function timeout(ms) { return new Promise((res, rej) => { // setTimeout(res, ms, 'done'); if (true) { res('成功了'); } else { rej('失败了'); } }) } let p = timeout(2000); p.then((val) => { console.log(val); }) // then方法可以接受两个回调函数作为参数,第一个回调函数时Promise对象的状态变成resloved时调用,第二个回调函数时Promise对象的状态变成rejected时调用。这两个函数都是可选的,不一定要提供,它们都是接收Promise对象传出的值作为参数;
-
-
5、Promise使用方法
链式实现:
// 在上面代码中 是实现了new出一个对象 定义了个常量来接收了 不用常量接收直接使用时
let num = 2;
new Promise((r,e) => { // r(resolve) e(reject)
// 异步逻辑代码
num === 1 ? r(num) : e(num) // 同一个逻辑中只能执行一种状态方式
}).then(res => {
console.log(`num,num为:${res}`)
}).catch(err => {
console.log(`num,num为:${err}`)
})
then方法解释:
// then方法是new时返回对象里的方法 也就是Promise里内置的一些方法
// then可以有第二个回调参数 这个回调参数就是catch
let num = 1;
new Promise((r,e) => {
// 异步逻辑代码
num === 1 ? r(num) : e(num)
}).then(res => { // 为r(resolve)调用时执行的方法
console.log(`num为1时被调用,num为:${res}`)
}, rej => { // 为e(reject)调用时执行的方法
console.log(`num为2时被调用,num为:${rej}`)
})
// 在then方法里 会返回一个新的Promise 这就是Promise链式调用的本质
// 在then里如果返回的是一个普通值(数字/字符串/普通对象/undefined) 那么这个返回值就会作为这个新返回对象里的resolve调用时传参的值 此时再链式调用then方法调用的就是新Promise对象的方法
// 如果返回的是一个Promise,当前状态就会变成返回Promise里的状态 同理返回一个对象 里面包含then方法时 也会改变状态
catch方法解释:
// catch方法在new的Promise中的回调函数里调用第二个参数(reject)时,就会回调catch方法里的回调参数 (也可是then中的第二个回调参数)。
// 在在new的Promise中的回调函数抛出异常时也会回调这个回调参数
const num = 1;
new Promise((res,rej) => {
if(num === 2) {
res(num);
}else {
// rej(num); // 此时就会回调catch里的会调参数
throw new Error('num不为2',num); // 此时也会回调catch里的会调参数
}
}).then(res => {
console.log(`num等于2时:${res}`)
}).catch(err => {
console.log(`num不等于2时:${err}`)
})
finally方法解释:
// finally 无论什么状态都会执行
let num = 1;
new Promise((res,rej) => {
num === 1 ? res(num) : rej(num);
}).then(res => {
console.log(`num等于1时:${res}`)
}).catch(err => {
console.log(`num不等于1时:${err}`)
}).finally(() => {
console.log(`都会执行`)
})
Promise类上的方法 all / allSettled:
// all方法可以多个Promise一起使用 统一返回所有结果
let num = 1;
let p1 = new Promise((r,t) => {
num === 1 ? r(num) : t(num);
})
let p2 = new Promise((r,t) => {
num === 1 ? r(num) : t(num);
})
let p3 = new Promise((r,t) => {
num === 1 ? r(num) : t(num);
})
Promise.all([p1,p2,p3]).then((res) => {
console.log(res); // [1,1,1] 以数组的方式返回所有结果
})
// 当其中一个抛出异常 或者执行t(reject)方法时会在执行处阻断Promise,下面的Promise执行就会被阻断
// 如若不乡阻断,可以用类allSettled方法
Promise.allSettled([p1,p2,p3]).then((res) => {
console.log(res); // 以数组的方式返回所有结果
})
以上是全部Promise对象!!!自行实验一下