什么是Promise
Promise 是异步编程的一种解决方案,比传统的解决方案回调函数和事件更合理和更强大。
所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。
(1)Promise的实例有三个状态:
Pending(进行中)
Resolved(已完成)
Rejected(已拒绝)
当把一件事情交给promise时,它的状态就是Pending,任务完成了状态就变成了Resolved、没有完成失败了就变成了Rejected。
(2)Promise的实例有两个过程:
pending -> fulfilled : Resolved(已完成)
pending -> rejected:Rejected(已拒绝)
需要注意:一旦从进行状态变成为其他状态就永远不能更改状态了。
特点:
- 将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。流程更加清晰,代码更加优雅。
- Promise对象提供统一的接口,使得控制异步操作更加容易。
缺点:
- 无法取消Promise,一旦新建它就会立即执行,无法中途取消。
- 如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。
- 当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
实例:
当我们在构造 Promise 的时候,构造函数内部的代码是立即执行的:
new Promise((resolve, reject) => {
console.log('new Promise')
resolve('success')
})
console.log('finifsh')
// new Promise -> finifsh
Promise 实现了链式调用,也就是说每次调用 then 之后返回的都是一个 Promise,并且是一个全新的 Promise,原因也是因为状态不可变。如果你在 then 中 使用了 return,那么 return 的值会被 Promise.resolve() 包装
Promise.resolve(1)
.then(res => {
console.log(res) // => 1
return 2 // 包装成 Promise.resolve(2)
})
.then(res => {
console.log(res) // => 2
})
当然了,Promise 也很好地解决了回调地狱的问题,可以把之前的回调地狱例子改写为如下代码:
ajax(url)
.then(res => {
console.log(res)
return ajax(url1)
}).then(res => {
console.log(res)
return ajax(url2)
}).then(res => console.log(res))
前面都是在讲述 Promise 的一些优点和特点,其实它也是存在一些缺点的,比如无法取消 Promise,错误需要通过回调函数捕获。
Promise基础
1、下面代码的执行结果是
const promise = new Promise((resolve, reject) => {
console.log(1);
console.log(2);
});
promise.then(() => {
console.log(3);
});
console.log(4);
最后应该输出1 2 4。这样最主要的就是3,要知道promise.then是微任务,会在所有的宏任务执行完之后才会执行,同时需要promise内部的状态发生变化,因为这里内部没有发生变化,所以不输出3。
2、下面代码的执行结果是
const promise1 = new Promise((resolve, reject) => {
console.log('promise1')
resolve('resolve1')
})
const promise2 = promise1.then(res => {
console.log(res)
})
console.log('1', promise1);
console.log('2', promise2);
输出结果如下:
'promise1'
'1' Promise{<resolved>: 'resolve1'}
'2' Promise{<pending>}'
'resolve1'
需要注意的是,直接打印promise1,会打印出它的状态值和参数。
这里说一下这道题的具体思路:
- script是一个宏任务,按照顺序执行这些代码
- 首先进入Promise,执行该构造函数中的代码,打印promise1
- 碰到resolve函数, 将promise1的状态改变为resolved, 并将结果保存下来
- 碰到promise1.then这个微任务,将它放入微任务队列
- promise2是一个新的状态为pending的Promise
- 执行同步代码1, 同时打印出promise1的状态是resolved
- 执行同步代码2,同时打印出promise2的状态是pending
- 宏任务执行完毕,查找微任务队列,发现promise1.then这个微任务且状态为resolved,执行它。
这样,就执行完了所有的的代码。
3、下面代码的执行结果是
const promise = new Promise((resolve, reject) => {
console.log(1);
setTimeout(() => {
console.log("timerStart");
resolve("success");
console.log("timerEnd");
}, 0);
console.log(2);
});
promise.then((res) => {
console.log(res);
});
console.log(4);
- 首先遇到Promise构造函数,会先执行里面的内容,打印1
- 遇到steTimeout,它是一个宏任务,被推入宏任务队列
- 接下继续执行,打印出2
- 由于Promise的状态此时还是pending,所以promise.then先不执行
- 继续执行下面的同步任务,打印出4
- 微任务队列此时没有任务,继续执行下一轮宏任务,执行steTimeout
- 首先执行timerStart,然后遇到了resolve,将promise的状态改为resolved且保存结果并将之前的promise.then推入微任务队列,再执行timerEnd
- 执行完这个宏任务,就去执行微任务promise.then,打印出resolve的结果
4、 下面代码的执行结果是
Promise.resolve().then(() => {
console.log('promise1');
const timer2 = setTimeout(() => {
console.log('timer2')
}, 0)
});
const timer1 = setTimeout(() => {
console.log('timer1')
Promise.resolve().then(() => {
console.log('promise2')
})
}, 0)
console.log('start');
这个题目就有点绕了,下面来梳理一下:
- 首先,Promise.resolve().then是一个微任务,加入微任务队列
- 执行timer1,它是一个宏任务,加入宏任务队列
- 继续执行下面的同步代码,打印出start
- 这样第一轮的宏任务就执行完了,开始执行微任务,打印出promise1
- 遇到timer2,它是一个宏任务,将其加入宏任务队列
- 这样第一轮的微任务就执行完了,开始执行第二轮宏任务,指执行定时器timer1,打印timer1
- 遇到Promise,它是一个微任务,加入微任务队列
- 开始执行微任务队列中的任务,打印promise2
- 最后执行宏任务timer2定时器,打印出timer2
这个题目还是比较复杂的,值得去认真理解一下。'start'
'promise1'
'timer1'
'promise2'
'timer2'
5、 下面代码的执行结果是
执行结果为:then:success1const promise = new Promise((resolve, reject) => {
resolve('success1');
reject('error');
resolve('success2');
});
promise.then((res) => {
console.log('then:', res);
}).catch((err) => {
console.log('catch:', err);
})
这个题目考察的就是Promise的状态在发生变化之后,就不会再发生变化。开始状态由pending变为resolve,说明已经变为已完成状态,下面的两个状态的就不会再执行,同时下面的catch也不会捕获到错误。