余生很贵,努力活成自己想要的样子,愿你能穿运动鞋撸铁汗如雨下,也能穿高跟鞋潇洒貌美如花,不负青春,不负自己。
哈哈~ 认真起来的样子,很有魅力嘛 ☺️☺️☺️
Promise
基础语法及使用
Promise 定义
Promise 对象用于表示一个异步操作的最终完成 (或失败)及其结果值。ES6中新增一个内置的类:Promise 承诺/约定模式,基于这种模式可以有效的处理异步编程
Promise 描述
一个 Promise 对象代表在这个 promise 被创建出来时不一定已知的值。它让您能够把异步操作最终的成功返回值或者失败原因和相应的处理程序关联起来。 这样使得异步方法可以像同步方法那样返回值:异步方法并不会立即返回最终的值,而是会返回一个 promise实例
Promise 方法
Promise.all(iterable)
- 语法:
Promise.all(iterable) - 参数:一个可迭代对象
- 返回值:返回一个
Promise实例- 此实例在
iterable参数内所有的promise都“完成(resolved)”或参数中不包含promise时回调完成(resolve);Promise.all返回的promise异步地变为完成。 - 如果参数中
promise有一个失败(rejected),此实例回调失败(reject),失败的原因是第一个失败promise的结果。 - 传入的可迭代对象为空,
Promise.all会同步地返回一个已完成(resolved)状态的promise。看下面例子
- 此实例在
Promise.any()
- 语法:
Promise.any(iterable); - 参数:一个可迭代的对象
- 返回值:只要其中的一个
promise成功,就返回那个已经成功的promise
Promise.race()
- 语法:
Promise.race(iterable); - 参数:一个可迭代的对象
- 返回值:返回一个
promise,一旦迭代器中的某个promise解决或拒绝,返回的promise就会解决或拒绝。(只要执行一个成功或者失败,返回的 promise 实例则是成功或者失败)
Promise.reject()
- 语法:
Promise.reject(reason); - 参数:表示Promise被拒绝的原因
- 返回值:返回一个失败原因为
reason的promise实例
Promise.resolve()
- 语法:
Promise.reject(value); - 参数:
Promise对象解析的参数,也可以是一个Promise对象,或者是一个thenable - 返回值:返回一个带着给定值解析过的
Promise对象,如果参数本身就是一个Promise对象,则直接返回这个Promise对象。
Promise.prototype.catch()
- 语法:
p.catch(onRejected);p.catch(function(reason) {});
- 参数:
.then()方法注入的第二个函数reason - 返回值:
catch()方法返回一个Promise,并且处理拒绝的情况
Promise.prototype.finally()
- 描述:返回一个
Promise。在promise结束时,无论结果是fulfilled或者是rejected,都会执行指定的回调函数 - 语法:
p.finally(onFinally);p.finally(function() {// 返回状态为(resolved 或 rejected)});
- 参数:
Promise结束后调用的Function。 - 返回值:返回一个设置了
finally回调函数的Promise对象。
Promise.prototype.then() 「下翻看详细使用」
- 描述:then() 方法返回一个 Promise。它最多需要有两个参数:Promise 的成功和失败情况的回调函数。
- 语法:
p.then(onFulfilled[, onRejected]);p.then(value => {// fulfillment}, reason => {// rejection});
- 参数:
onFulfilledonRejected
- 返回值:返回一个设置了
finally回调函数的Promise对象。
Promise 的三大概念
executor函数
new Promise的时候会立即执行executor函数,在executor函数中管理了一个异步编程代码(同步代码也是OK的),此时Promise状态是pending;在我们异步操作成功或者失败的时候,通过执行resolve或者reject函数,可以有效的把实例状态变为成功状态或者失败状态,也就是从pending变为fulfilled(成功)/rejected(失败);如果代码报错的情况下,也是把状态改为rejected(失败);
- 实例
- 每一个 Promise 实例都具备
[[PromiseState]]和[[PromiseResult]]方法,[[PromiseState]]是promise状态:pending、fulfilled/resolve、rejected三种状态;[[PromiseResult]]是promise值:默认是undefined,一般存储成功的结果或者失败的原因。
Promise原型上的方法.then(): 用来管控成功或者失败的操作
- 实例可以调用
.then(),它会存放两个方法:result和reason(都是函数); 当p1实例的状态修改为fulfilled的时候,通知传递的第一个函数(result)执行,result就是[[PromiseResult]]的值; 当p1实例的状态修改为rejected的时候,通知传递的第二个函数(reason)执行,reason就是[[PromiseResult]]的值 - 不论是否基于
THEN注入了方法,执行resolve/reject的时候「修改状态和值」是同步的会立即处理,但是「通知对应注入方法执行」的这个任务是异步操作的,不会立即处理,只是把它排在等待任务队列中,当其他事情处理完,再次返回去,通知对应注入的方法执行 .then(onfulfilled, onrejected): 执行then方法只是把onfulfilled/onrejected函数保存起来了(这个保存的操作是同步的),但是此时函数还没有执行,当promise状态变为成功或者失败的时候,才会去触发执行对应的函数,then是一个异步的微任务
Promsie 是如何管控异步编程的
new Promise创建的实例,其状态和结果,取决于:executor函数中的resolve/reject执行executor函数执行是否报错
- 执行
.then方法返回一个全新的promise实例
new Promise的时候创建一个promise实例,此时在executor函数中管理一套异步的代码- 后期等异步操作成功或者失败的时候,执行
resolve/reject,以此来控制promise实例的状态和结果 - 根据状态和结果,就可以控制基于
.then注入的两个方法中的哪一个去执行了
在看栗子
- 下述代码执行的顺序(
executor函数管控的是异步编程)new Promise构造函数执行- 执行
executor函数:设置一个异步定时器 - 执行实例
.then(result, reason)注入两个方法,注入的方法会保存起来(此时这两个方法还没有执行) - 等待
1000ms - 执行定时器的回调函数:通过执行
resolve改变promise状态和值 - 通知之前基于
.then()注入的两个方法中的某一个执行
let p1 = new Promise(function (resolve, reject) {
// new Promsie 的时候立即执行 executor 函数,在 executor 函数中管理了一个异步编程代码(此时状态是 pending);当异步操作到达指定时间,开始执行的时候(可以理解为异步操作成功),此时我们通过执行 resolve,把 promise 状态修改为 fulfilled;
setTimeout(function(){
resolve('OK');
}, 1000)
});
console.log(p1); // Promise {<pending>}__proto__: Promise[[PromiseState]]: "pending"[[PromiseResult]]: undefined
p1.then(result=>{
// 当p1 实例的状态修改为 fulfilled 的时候,通知传递的第一个函数执行,result 就是[[PromiseResult]] 的值
console.log('成功', result)
}, reason=>{
// 当p1 实例的状态修改为 rejected 的时候,通知传递的第二个函数执行,reason 就是[[PromiseResult]] 的值
console.log('失败', reason)
});
下述代码管控的顺序(
executor函数管控的是同步代码)
new Promise构造函数执行- 执行
executor函数:- 输出
1 - 立即修改状态和值,并且通知基于
THEN注入的方法执行,此时.THEN方法还没有执行,方法还没有被注入,不知道该通知谁来执行,所以此时需要把通知方法执行的操作先保存起来,放入到等待任务队列中,这个操作本身是异步的,需要等待方法注入完成后再通知其执行 - 输出
2(到此executor函数已经执行完成)
- 输出
- 执行实例
.then(result, reason)注入两个方法,注入的方法会保存起来(此时这两个方法还没有执行) - 输入
3 - 通知之前基于
.then()注入的两个方法中的第一个执行(result方法)
let p1 = new Promise((resolve, reject)=>{
console.log('1');// 1
resolve('OK');
console.log('2');
});
p1.then(result=>{
console.log('成功:'+result);
}, reason=>{
console.log('失败:'+reason);
});
console.log('3');
new Priomise 内部机制
new Promise的时候会立即执行传递的executor函数- 在
executor函数中一般用来管控一个异步的操作(同步亦可) - 而且传递给
executor函数两个参数:resolve、reject,并且两个参数都是函数 - 创造
Promise类的一个实例p1,每一个Promise的实例都存在两个属性[[PromiseState]] promise状态:pending: 准备状态fulfilled(旧版本浏览器)/resolved(新版本浏览器): 成功状态(已兑现)rejected: 失败状态(已拒绝)- 一旦状态从
pending改变为fulfilled或者是rejected, 都无法再次改变其状态
[[PromiseResult]] promise值- 默认是
undefined,一般存储成功的结果或者失败的原因 - 每个
Promise实例指向Promise类的原型p1.__proto__ = Promise.prototype Promise的原型上的方法:then/catch/finally- 如果
executor函数中的代码执行报错,则实例的状态也会变为失败,并且[[PromiseResult]]是报错的原因 看下面代码示例
- 默认是
new Promise 的时候必须要传入一个函数( executor 函数),否则报错
let p1 = new Promise();
console.log(p1);// 报错 Promise resolver undefined is not a function at new Promise
new Promise 的时候会立即执行传递的executor函数,
执行 resolve 控制实例的状态变为成功,传递的值 100,是成功的结果
[[PromiseState]]: 'fufilled'
[[PromiseResult]]: 100
let p1 = new Promise(function (resolve, reject) {
resolve(100);
});
p1.then();
执行 reject 控制实例的状态变为失败,传递的值 NO,是失败的结果
[[PromiseState]]: 'rejected'
[[PromiseResult]]: NO
let p1 = new Promise(function (resolve, reject) {
reject('NO');
});
p1.then();
THEN 方法
当前实例的状态已经是成功或者失败,此时创建一个异步的微任务,等待同步任务结束,根据成功还是失败,来决定执行哪个方法
- 如果此时的状态还是
pending,则直接把方法存储起来即可,没有创建异步的微任务 - 如果此时的状态已经改变
(fulfilled/rejected),那么执行resolve/reject的时候,会创建一个异步的微任务,等待同步任务结束后,根据状态去执行基于.then动态存储的函数
看栗子
let p1 = new Promise(resolve => {
// 1000ms 后,开始执行定时器,此时p1.then()中的两个方法已经注入完成
setTimeout(function(){
// 执行 resolve ,立即修改 promise 状态和值
resolve('OK);
console.log(1);
}, 1000)
});
p1.then(result=>{
console.log(2);
});
// 最终结果为 1 2
- 输出
1 2- 说明
then方法为异步:执行resolve后,不会等待执行then方法中的result,而是放在一个事件队列中,先把同步代码执行完成输出1,在去事件队列中找异步代码去执行输出2
- 说明
结论:不论是否基于 THEN 注入了方法,执行 resolve/reject 的时候「修改状态和值」是同步的会立即处理,但是「通知对应注入方法执行」的这个任务是异步操作的,不会立即处理,只是把它排在等待任务队列中,当其他事情处理完,再次返回去,通知对应注入的方法执行
.then() 分为两种情况执行
.then(onfulfilled, onrejected)返回的新Promise实例,其成功和失败取决于:onfulfilled/onrejected执行是否报错以及返回结果(不管执行onfulfilled还是onrejected, 只要执行不报错,则返回的新实例就是成功,[[PromiseResult]]就是返回给实例的结果)
let p1 = new Promise((resolve, reject)=>{
setTimeout(()=>{
<!-- resolve('OK'); -->
<!-- reject('OK'); -->
}, 1000);
});
let p2 = p1.then(result=>{
console.log('成功', result);
return 10;
}, reason=>{
console.log('失败', reason);
return 0;
})
console.log(p2);
执行 resolve('OK');
p2 是一个新的 promise 实例,它的 [[PromiseState]] 为 fulfilled;[[PromiseResult]] 为 10
执行 reject('NO');
p2 是一个新的 promise 实例,它的 [[PromiseState]] 为 fulfilled;[[PromiseResult]] 为 0
then方法返回结果是一个全新的 Promise 实例,则这个实例的成功和失败等内容决定了p2的成功和失败等内容
let p1 = new Promise((resolve, reject)=>{
setTimeout(()=>{
resolve('OK');
<!-- reject('OK'); -->
}, 1000);
});
let p2 = p1.then(result=>{
console.log('成功', result);
return new Promise((resolve, reject)=>{
<!-- reject('新实例'); -->
resolve('新实例');
})
}, reason=>{
console.log('失败', reason);
return 0;
})
执行 .then 方法返回一个新的 promise 实例(promise1),return 的结果又是一个新的 promise 实例(promise2)的话,那么最终 p2 的结果等同于 return 的 promise2 执行之后的结果
执行 reject('新实例');
console.log(p2);
p2 是一个新的 promise 实例,它的 [[PromiseState]] 为 rejected;[[PromiseResult]] 为 '新实例'
执行 resolve('新实例');
console.log(p2);
p2 是一个新的 promise 实例,它的 [[PromiseState]] 为 fulfilled;[[PromiseResult]] 为 '新实例'
Promise.resolve/reject: 直接返回指定状态的 promise 实例
Promise.all: 取决于多个 Promise 实例中是否出现有失败的
async await
async function 的定义:async函数是使用async关键字声明的函数。
async函数是AsyncFunction构造函数的实例, 并且其中允许使用await关键字。
async和await关键字让我们可以用一种更简洁的方式写出基于Promise的异步行为,而无需刻意地链式调用promise。
async函数还可以被作为表达式来定义。
async函数可能包含0个或者多个await表达式。
async await 「ES7 新增的」
async:用来修饰函数,最后默认让函数返回一个 promise 实例(函数执行报错,实例状态是失败,结果是报错原因;否则实例状态是成功,结果是 return 后面的值), 返回一个 promise 实例的好处是把一个普通函数执行,执行后的结果默认让 promise 来管控,可以调用 .then 方法来处理逻辑,实现链式调用
async函数一定会返回一个promise对象。如果一个async函数的返回值看起来不是promise,那么它将会被隐式地包装在一个promise中。
async function fn() {
return 10;
}
console.log(fn());
等价于
async function fn() {
return Promise.resolve(10);
}
返回一个promise 实例,[[PromiseState]]: 'fulfilled', [[PromiseResult]]: '10'
async function fn() {
if(true) throw new TypeError('11');
}
console.log(fn());
返回一个 promise 实例,[[PromiseState]]: 'rejected', [[PromiseResult]]: TypeError '11'
async function fn() {
setTimeout(()=>{
return 10;
}, 1000)
}
console.log(fn());
执行fn()输出:
返回一个promise实例,[[PromiseState]]: 'fulfilled', [[PromiseResult]]: undefined
再看栗子
- 从第一行代码直到(并包括)第一个await表达式(如果有的话)都是同步运行的
- 如果函数体内有一个await表达式,async函数就一定会异步执行。
async function foo() {
await 1
}
等价于
function foo() {
return Promise.resolve(1).then(() => undefined)
}
结论:不能管控异步操作,只是用来修饰函数,使其返回一个 promise 实例,可以调用 .then 方法。
不管执行 resolve 还是 reject,只要代码不报错则为成功,[[PromiseState]] 则为 fulfilled; [[PromsieResult]] 是 return 的结果
await
await 操作符用于等待一个 Promise 对象。它只能在异步函数 async function 中使用。
await 的定义:await 表达式会暂停当前 async function 的执行,等待 Promise 处理完成。若 Promise 正常处理(fulfilled),其回调的resolve函数参数作为 await 表达式的值,继续执行 async function。
使用范围:await关键字只在async函数内有效。如果你在async函数体之外使用它,就会抛出语法错误 SyntaxError 。
async/await的目的:为了简化使用基于promise的API时所需的语法。async/await的行为就好像搭配使用了生成器和promise。
await 执行顺序:await表达式会暂停整个async函数的执行进程并出让其控制权,只有当其等待的基于promise的异步操作被兑现或被拒绝之后才会恢复进程。promise的解决值会被当作该await表达式的返回值。使用async / await关键字就可以在异步代码中使用普通的try / catch代码块。
await返回值:一般 await 后面放的都是 promise 实例,如果设置的不是 promise 实例,返回该值本身;
await返回值分两种情况:
- 第一种情况:正常的值
await 10, 它会默认返回一个promise实例默认成功状态,结果是return后面的值 - 第二种情况:函数执行
await fn()- 先立即执行
fn()函数,接收函数的返回值 - 判断
await返回值是否是promsie实例 - 如果不是,则根据以上两种情况分析即可
- 先立即执行
来个 EG ↓
await 后的表达式是 promise 实例
Promise正常处理,状态为(pending -> fulfilled), 回调的resolve函数参数作为await表达式的值,继续执行async function
async function fn() {
let result = await new Promise((resolve, reject)=>{
setTimeout(()=>{
resolve(10);
}, 1000)
});
console.log(result);
}
console.log(fn());
执行 fn(); 输出:1000ms 后输出10
Promise处理异常( pending -> rejected ),await表达式会把Promise的异常原因抛出
let p1 = new Promise((resolve, reject)=>{
reject('2');
})
async function f1() {
let result = await p1;
console.log(result);
}
f1();
输出
Uncaught (in promise) 2
await 后的表达式不是 promise 实例
- 第一种情况:
await后的表达式是基本值
async function fn() {
// 等同于 let result = await Promise.resolve(10);
let result = await 10;
console.log(result);
}
fn();
执行 fn(), 输出: 10
执行 console.log( fn() ); 输出一个 promise 实例「证实:async 会默认让函数返回一个 promise 实例」
- 第二种情况:
await后的表达式是函数
function func() {
return new Promise(resolve =>{
resolve(2);
})
};
async function fn() {
let result = await func();
console.log(result);
};
fn();
输出
2
await 后的表达式是一个函数 func, 执行顺序为:
- 立即执行
func()函数,await来接收func()的返回值 await后的表达式是一个promise实例,并且promise状态为fulfilled,则回调的resolve函数参数就是await表达式的值;也就是let result = await 2;- 继续执行
async function - 输出
result值为2
await是一个异步的微任务,把当前上下文中await下面要执行的代码整体存储到异步的微任务中,当await后面的promise实例状态为成功后,再去执行下面的代码(也就是那个异步的微任务)
综合 EG
function func() {
console.log(1);
return new Promise(resolve =>{
setTimeout(()=>{
resolve(2);
}, 1000)
})
};
console.log(3);
async function fn() {
console.log(4);
let result = await func();
console.log(result);
console.log(5);
};
fn();
console.log(6);
执行顺序:
- 浏览器自上而下执行,首先执行的是全局上下文中的同步代码,输出:
3 - 执行函数
fn(),async函数体内,从第一行代码直到第一个await表达式都是同步运行的。输出:4 await表达式之后的代码可以被认为是存在链式调用的then回调中,也就是把当前上下文中await下面要执行的代码整体存储到异步的微任务中,当await后面的promise实例状态为成功后,再去执行下面的代码。此时,EventQueue中微任务队列中存在一个任务。func()函数体中的同步代码,输出:1func()返回的结果是一个异步的宏任务,放在EventQueue中宏任务队列中。- 浏览器不会去等待,会继续接着往下执行。输出:
6 - 同步代码执行完毕,浏览器会执行
Eventqueue中的微任务队列, 查看是否有可执行的任务 - 此时,
[[PromiseState]]的状态为pending, 所以微任务是一个不可执行的任务。 - 那么接着找宏任务队列,过
1000ms执行resolve(2), 执行完毕,[[PromiseState]]状态从pending变为fulfilled,宏任务执行完毕。 - 浏览器继续到微任务队列中查找可执行的代码,
promise状态变为成功,await后的代码可以执行,微任务队列中的代码执行,输出:2 5 - 若
EventQueue中没有任务,则执行结束。最终输出:3 4 1 6 2 5
在await表达式之后的代码可以被认为是存在在链式调用的then回调中,多个await表达式都将加入链式调用的then回调中,返回值将作为最后一个then回调的返回值。
注意:promise链是分阶段构造的,因此在处理异步函数时必须注意对错误函数的处理。一般我们在 promise 链上配置了.catch处理程序,最后捕捉所有的错误
对失败的 promise 实例没有做异常处理,则控制台抛出异常,但是不会影响后续代码的执行
async function fn() {
let result = await Promise.reject(1);
console.log(result);
setTimeout(()=>{
console.log('定时器1')
})
}
fn();
setTimeout(()=>{
console.log('定时器2')
})
await 后的代码不执行, 但不影响后续代码的执行,输出
Uncaught (in promise) 1
定时器2
promise 实例处理代码抛异常的方法:promise.catch(reason=>{})
async await 处理代码抛异常的方法: try{...}catch(err){..}
如果直接使用以下方式处理异常:await 接收的是Promise.reject(1),如果调用.catch() 会返回一个新的 promise 实例,会与原意不符
let result = await Promise.reject(1).catch();
这种方式会报错
let i = 0;
function fn() {
console.log(++i);
fn();
}
fn();// Maximum call stack size exceeded
不会报错,但是会形成死循环。在 EventLoop 机制中,只有主线程空闲才会执行异步的任务
每次调用 await ,都会形成一个异步的微任务,这里涉及两个队列到同步异步之间的切换,所以浏览器并不会报错
let i = 0;
async function fn() {
console.log(++i);
await Promise.resolve('OK');
fn();
}
fn();