错误之处,欢迎指正。
1. 简介
在学习Promise
之前,需要搞清楚JavaScript
的异步机制,了解事件循环机制以及事件队列。
在Promise
出现之前,我们都是使用回调函数的方式来进行异步操作,随着前端功能越来越复杂,就可能出现回调地狱(当前的异步操作需要等待之前的异步操作完成才能执行);回调也不能良好的处理有联系的异步操作,例如某个异步操作需要等待多个异步操作完成。
Promise
是ES6
中提出的异步解决方案,本质上Promise
是一个构造函数,通过new Promise()
的形式来创建一个实例。
2. Promise
- 两个阶段
-
unsettled
未决阶段 -
settled
已决阶段
- 三种状态
-
pending
等待状态 -
resolved
成功状态 -
rejected
失败状态
- 两个过程
-
resolve
过程
pending
→resolved
-
reject
过程
pending
→rejected
(代码执行错误和抛出错误,都会进入`rejected·状态)
-
Promise
基础用法
Promise
并没有消除回调,而是使用了特定的模式来让回调可控,从而解决回调地狱等问题。
const pro = new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() > 0.1) {
resolve('没中奖');
} else {
resolve('中奖了');
}
}, 100);
})
pro.then(data => {
console.log(data);
})
pro.catch(err => {
console.log(err);
})
注意:在未决阶段的处理函数中,如果发生了未捕获的错误,会将状态推向rejected
。
-
Promise
串联
通过串联这种方式,良好的解决了回调地狱的嵌套问题。
这里要记住,Promise
中,无论是then
方法还是catch
方法,返回值都是一个全新的Promise
,因此才产生了Promise
串联模式。
const pro = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('123')
}, 1000);
})
const pro2 = pro.then(data => {
console.log(data); //123
return 456
})
pro2.then(data => {
console.log(data); //456
})
console.log(pro2); //pending
如果pro.then
中抛出了错误,那么pro2
为rejected
状态;如果没有抛出错误,则把pro.then
中的函数返回值作为resolved
的传递值传给pro2
。
const pro1 = new Promise((resolve, reject) => {
resolve(1);
})
const pro2 = new Promise((resolve, reject) => {
resolve(2);
})
const pro3 = pro1.then(data => {
return pro2;
})
pro3.then(data => {
console.log(data); //2
})
如果Promise
的处理函数返回的是一个Promise
,那么新的Promise
要和返回的Promise
状态和数据保持一致。例如上述代码中,pro1
的后续处理返回的是pro2
,那么pro3
就要和pro2
的状态以及数据保持一致,因此返回了2
。
-
finally
函数
const pro = new Promise((resolve, reject) => {
resolve(true)
})
pro.then(data => {
console.log(data);
})
pro.finally(() => { //没有参数
console.log('pro执行完毕');
})
当Promise
达到已决状态就会执行函数finally
【ES2018】。
-
resolve
和reject
静态方法
const pro = Promise.resolve(1);
pro.then(data => {
console.log(data); //1
})
const pro2 = Promise.reject('error');
pro2.catch(message => {
console.log(message); //error
})
Promise
还提供了两个静态方法resolve
和reject
,这两个方法都会直接返回一个新的Promise
。
特殊情况:
const pro = new Promise(resolve => {
resolve(1);
})
const pro2 = Promise.resolve(pro);
pro2.then(data => {
console.log(data); //1
})
console.log(pro === pro2); //true
如果Promise.resolve
传递的参数是一个Promise
,那么直接返回被传递的Promise
。(谷歌浏览器)
all
const pro1 = new Promise(resolve => {
resolve(1);
})
const pro2 = new Promise((resolve, reject) => {
resolve('error');
})
const arr = [pro1, pro2];
const newPro = Promise.all(arr);
newPro.then(data => {
console.log(data); //[1, 'error']
})
all
方法接收一个由多个Promise
组成的可迭代对象,返回一个新的Promise
对象,只有当参数中的所有Promise
为resolved
状态时,该方法返回的新的Promise
才会为resolved
状态,只要有一个不是resolved
状态,那么新的Promise
就为rejected
状态;resolved
状态下,新的Promise
的数据是参数中Promise
数据组成的数组,rejected
状态下,新的Promise
的数据是参数中rejected
状态的Promise
数据。
race
const pro1 = new Promise((resolve, reject) => {
reject(1);
})
const pro2 = new Promise((resolve, reject) => {
resolve('error');
})
const arr = [pro1, pro2];
const newPro = Promise.race(arr);
newPro.then(data => {
console.log(data);
}, message => {
console.log(message); //1
})
race
方法与all
方法类似,不同的是,只要参数中有一个为已决状态状态,那么race
返回的Promise
就是已决状态,并且和参数中的状态保持一致。
3. 在setTimout
里执行Promise
setTimeout
在宏队列,relove
、reject
在微队列,微队列先执行,宏队列后执行。
console.log(1);
setTimeout(() => {
console.log(3)
}, 0);
console.log(2);
上述代码最终输出结果为1
,2
,3
;因为JavaScript
语言的执行机制是先执行同步代码,同步代码执行完毕执行异步代码。
console.log(1);
setTimeout(() => {
const pro = new Promise((resolve, reject) => {
console.log(3);
resolve(4);
}).then(data => {
console.log(data);
})
}, 1000);
console.log(2);
上述代码最终输出结果为1
,2
,3
,4
;先输出1
,然后定时器进入宏队列,然后输出2
,此时开始执行异步代码,微队列为空,执行宏队列里面的定时器,定义pro
是同步代码,输出3
,然后resolve
进入微队列,同步代码执行完毕,执行异步代码resolve
,输出4
。
4. 手写Promise
- 涉及到的知识点:
Class
、Symbol
、立即执行函数、高阶函数。 - 大体步骤
- 首先要实现
Promise
的状态和数据的改变 - 其次是推向已决状态的实现
- 然后是串联实现(比较难的一部分)
- 最后是实现
Promise
的静态方法
- 代码
/*
1. 初始化,pending状态以及值的设定
2. 执行pending状态的处理函数
3. 在构造器中定义resolve和reject函数
4. 捕获pending状态处理函数的异常,如果有错误,直接推向reject
5. 定义状态改变函数(将未决推向已决状态的函数)
6. then、catch处理函数定义,如果此时是未决状态,就把处理函数传递过来的参数存放至执行队列;如果是已决状态,就直接执行。
*/
const myPromise = (() => {
const PENDING = 'pending',
RESOLVED = "resolved",
REJECTED = "rejected",
PromiseValue = Symbol('PromiseValue'),
PromiseStatus = Symbol('PromiseStatus'),
changeStatus = Symbol('changeStatus'),
handle = Symbol('handle'),
thenables = Symbol('thenables'),
catchables = Symbol('catchables'),
linkPromise = Symbol('linkPromise');
return class myPromise {
/**
*
* @param {*} executor pending状态下的处理函数
*/
constructor(executor) {
this[PromiseStatus] = PENDING; //初始为pending状态
this[PromiseValue] = undefined; //此时没有数据
this[thenables] = []; //then执行队列
this[catchables] = []; //catch执行队列
const resolve = data => {
this[changeStatus](RESOLVED, data, this[thenables]);
} //resolve函数
const reject = message => {
this[changeStatus](REJECTED, message, this[catchables]);
} //reject函数
try {
executor(resolve, reject); //执行处理函数
} catch (error) { //错误状态推向rejected
reject(error)
}
}
/**
*
* @param {*} status 状态
* @param {*} value 数据
*/
[changeStatus](status, value, queue) { //推向已决状态
if (this[PromiseStatus] !== PENDING) {
return; //如果不是未决状态,直接返回不做处理
}
this[PromiseStatus] = status; //改变状态
this[PromiseValue] = value; //改变数据
queue.forEach(handler => {
handler(this[PromiseValue]); //执行队列后续处理执行
});
}
then(thenable, catchable) { //后续处理
return this[linkPromise](thenable, catchable);
}
catch (catchable) { //后续处理
return this[linkPromise](undefined, catchable);
}
[handle](status, handler, queue) {
if (this[PromiseStatus] === status) {
setTimeout(() => {
handler(this[PromiseValue]);
}, 0);
} else {
queue.push(handler);
}
}
[linkPromise](thenable, catchable) { //串联的实现
function executor(data, exec, resolve, reject) {
try {
const result = exec(data);
if (result instanceof myPromise) {
result.then(data2 => {
resolve(data2);
}, err => {
reject(err);
})
} else {
resolve(result)
}
} catch (error) {
reject(error)
}
}
return new myPromise((resolve, reject) => {
this[handle](RESOLVED, data => {
if (typeof thenable !== 'function') {
resolve(data);
return;
}
executor(data, thenable, resolve, reject)
}, this[thenables])
this[handle](REJECTED, message => {
if (typeof thenable !== 'function') {
reject(message);
return;
}
executor(message, catchable, resolve, reject)
}, this[catchables])
})
}
static resolve(data) {
if (data instanceof myPromise) {
return data;
} else {
return new myPromise((resolve) => {
resolve(data);
})
}
}
static reject(message) {
return new myPromise((resolve, reject) => {
reject(message);
})
}
static all(pros) {
return new myPromise((resolve, reject) => {
const result = pros.map(p => {
const obj = {
result: undefined,
status: false
};
p.then(data => {
obj.result = data;
obj.status = true;
const unResolved = result.filter(r => !r.status);
if (unResolved.length === 0) {
resolve(result.map(r => r.result))
}
}, message => {
reject(message);
})
return obj;
})
})
}
static race(pros) {
return new myPromise((resolve, reject) => {
pros.forEach(p => {
p.then(data => {
resolve(data);
}, message => {
reject(message);
})
})
})
}
}
})(); //使用立即执行函数来避免命名重复
5. async
和await
async
和await
是 [ES2016(ES7)] 新增的两个关键字。
-
async
用于简化Promise
的创建,用于修饰函数,放在函数最开始的位置,被修饰函数的返回结果一定是Promise
对象。
async function foo() {
return 2;
}
//等效于
function foo() {
return new Promise((resolve, reject) => {
resolve(2);
})
}
-
await
一定要放在async
函数中。
async function foo2() {
const result = await foo();
console.log(result); //2
}
//等效于
function foo2() {
return new Promise((resolve, reject) => {
foo().then(data => {
const result = data;
console.log(result); //2
})
})
}
- 特殊情况(面试有几率考)
async function foo() {
const result = await 1;
console.log(result); //1
}
//等效于
function foo() {
return new Promise((resolve, reject) => {
Promise.resolve(1).then(data => {
const result = data;
console.log(result);
})
resolve();
})
}