Promise
只有promise调用then的时候,then里面的函数才会被推入微任务中;
1.Promise的三个状态
pending(进行中)、fulfilled(已成功)、rejected(已失败)
- 只有异步操作的结果可以改变状态,其他任何操作都无法改变,即承诺
状态改变只有2种可能
- 1.1 pending->fulfilled
- 1.2 pending->rejected
状态改变就不会再变,如果改变已经发生,再对Promise对象添加回调函数也会立即得到结果
这与事件(Event)完全不同,即错过了它再去监听,是得不到结果的。
如果某些事件不断地反复发生,一般来说,使用 Stream 模式是比部署Promise更好的选择。
2.基本用法
Promise构造函数接收一个函数作为参数,该函数的两个参数分别为resolve和rejecte,也是函数。
- resolve函数的作用:将Promise对象的状态从“未完成”变为“成功”,并将异步操作的结果作为参数传递出去;
- rejecte函数的作用:将Promise对象的状态从“未完成”变为“失败”,将错误作为参数传递出去。
Promise实例生成以后,用then方法分别制定resolved状态和rejected状态的回调函数。
⬆️它们都接受Promise对象传出的值作为参数
3.Promise.prototype.then()
then方法返回一个新的Promise实例
前一个实例返回的参数可能还是一个Promise对象(即有异步操作),这时后一个回调函数会等待该Promise对象的状态发生变化才会被调用。
4.Promise.prototype.catch()
状态变为rejected时执行,另外then方法指定的回调函数如果运行中抛出错误也会被捕获
如果Promise的状态已经变为resolved了,再抛出错误是无效的。
和传统的try/catch不同,如果没有catch()指定错误处理,Promise对象抛出的错误不会传递到外部代码。不会有任何反应。
- catch方法返回的还是一个Promise对象,后面可以继续跟then()或者catch(),后面的then报错和前面的catch无关了。
5.Promise.prototype.finally()
不管如何,都会执行的代码,即then的特例,不管状态如何都会执行
Promise.prototype.finally = function (callback) {
let P = this.constructor;
return this.then(
value => P.resolve(callback()).then(() => value),
reason => P.resolve(callback()).then(() => { throw reason })
);
};
6.Promise.all()
用于将多个 Promise 实例,包装成一个新的 Promise 实例
- 对于Promise.all([p1,p2,p3]),只有p1、p2、p3的状态都为fulfilled,p的状态才会为fulfilled
7.Promise.race()
参数与Promise.all()一样,一旦迭代器中的某个promise解决或拒绝,返回的 promise就会解决或拒绝
8.Promise.allSettled()
只有等所有参数实例都返回结果,不管是fulfilled还是rejected,包装实例才会结束
9.Promise.resolve()
有时需要将现有对象转为 Promise 对象,Promise.resolve()方法就起到这个作用。
Promise中一些需要注意的地方
对于推迟
begin->1->2->3->then->4 可以发现then推迟了两个时序
// 推迟原因:浏览器会创建一个 PromiseResolveThenableJob 去处理这个 Promise 实例,这是一个微任务。
// 等到下次循环到来这个微任务会执行,也就是PromiseResolveThenableJob 执行中的时候,因为这个Promise 实例是fulfilled状态,所以又会注册一个它的.then()回调
// 又等一次循环到这个Promise 实例它的.then()回调执行后,才会注册下面的这个.then(),于是就被推迟了两个时序
then方法里面参数若不是函数,会发生值透传
onResolved = typeof onResolved === 'function' ? onResolved : value => value
var promise = new Promise((resolve, reject) => {
console.log(1)
})
// 没有reslove,then里面函数不会执行,但此处参数不是函数,发生值透出传,即value => value,判断是否为函数时顺便执行了log
promise.then( console.log(2) )
console.log(3)
// 1
// 2
// 3
Promise.resolve(1)
.then(2) // 非函数,值透传
.then(Promise.resolve(3)) // 非函数,值透传
.then(console.log)
// 1
Promises实现异步调度
/**
* 题目:JS 实现异步调度器
* 要求:
* JS 实现一个带并发限制的异步调度器 Scheduler,保证同时运行的任务最多有 2 个
* 完善下面代码中的 Scheduler 类,使程序能正确输出
*/
class Scheduler {
add(promiseCreator) {
// ...
}
// ...
}
const timeout = (time) => {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, time);
});
};
const scheduler = new Scheduler();
const addTack = (time, order) => {
return scheduler
.add(() => timeout(time))
.then(() => console.log(order));
};
addTack(1000, '1');
addTack(500, '2');
addTack(300, '3');
addTack(400, '4');
// 输出:2 3 1 4
// 一开始,1、2 两个任务进入队列
// 500ms 时,完成 2,输出 2,任务 3 进队
// 800ms 时,完成 3,输出 3,任务 4 进队
// 1000ms 时,完成 1,输出 1,没有下一个进队的
// 1200ms 时,完成 4,输出 4,没有下一个进队的
// 进队完成,输出 2 3 1 4
class Scheduler {
constructor(maxLen) {
this.maxLen = maxLen
this.taskList = []
this.count = 0
}
async add(promiseCreator) {
// ...
if (this.count >= this.maxLen) {
await new Promise((resolve) => {
this.taskList.push(resolve)
})
}
this.count++
let result = await promiseCreator()
this.count--
if (this.taskList.length) {
// 即执行resolve改变状态,解除阻塞,执行await后面的方法
this.taskList.shift()()
}
return result
}
// ...
}
const timeout = (time) => {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, time);
});
};
const scheduler = new Scheduler(2);
const addTack = (time, order) => {
return scheduler
.add(() => timeout(time))
.then(() => console.log(order));
};
addTack(1000, '1');
addTack(500, '2');
addTack(300, '3');
addTack(400, '4');