余生很贵,努力活成自己想要的样子,愿你能穿运动鞋撸铁汗如雨下,也能穿高跟鞋潇洒貌美如花,不负青春,不负自己。
哈哈~ 认真起来的样子,很有魅力嘛 ☺️☺️☺️
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});
- 参数:
onFulfilled
onRejected
- 返回值:返回一个设置了
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()
函数体中的同步代码,输出:1
func()
返回的结果是一个异步的宏任务,放在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();