在学习本章内容之前,需要先了解什么是异步编程 可以参看 JavaScript 异步编程 | 菜鸟教程
学习路线:
1:应用:
promise 是什么?
Promise是一个es6提供的类 , 目的是为啦更加优雅的书写复杂的异步任务。
因为Promise 是新增属性,所以都知道,一些旧版本不支持, 在官网可知:苹果的 Safari 10 和 Windows 的 Edge 14 版本以上浏览器才开始支持 ES6 特性。
- 再此我先讲一些知识点:
// bug: 只要是回调函数是就是异步的----> 错误的
// good: 以前异步程序都是用回调函数编写
利用时间戳和setInterval 写一个简单的 setTimeout
输出结果
解决了什么问题?
1:回调地狱问题
2:代码的可读性问题
3:信任问题
看一下回调地狱
在没有promise的时候 ,回调地狱太难受,痛点:可维护,可读性问题. 不是性能,不是内存问题
连环请求 : 痛点:太难看懂了. 所以就诞生了promise
特点是什么?
promise函数执行特点
有哪些内容? 3个特殊属性
promise实例有什么
- 总结: promise程序执行特点: 有哪些内容
- 回顾以前学过的promise
- 总结 async await
- 面试题:
谈谈你对promise的理解? 说一下你在开发中如何使用promise的
你是如何理解async await 函数的?
为什么需要异步程序同步化
扩展 es6有哪些新特点
状态有几个? 3个
原型api有哪些? 三个原型api : then() catch() finally()
原型api-then
console.log(Promise);
let p = new Promise((resolve,reject)=>{
resolve(123)
// reject()
})
console.log(p);
p.test = '测试p p1'
// console.log(Promise.prototype);
//api: then catch finally
// then
// 作用:1:处理状态变为 已接受的请开给你
// 2:获取p中[[PromiseResult]]的赋值
// 3:实参二:处理 已拒绝状态的业务(不常用)
// 返回值:全新的promise实例对象, 状态已接受,结果:undefined
// 优势:支持then 链式调用
// 弊端: then 停不下来
let p1 = p.then((res)=>{
// 作用: fulfilled(已接受) 状态情况下,处理的业务
// 函数什么时候执行呢?
// res 是什么呢? p.[[PromiseResult]]的赋值
console.log(res);
// 问题:return 作用是什么? 是p.then() 的返回值
return '给p1.[[PromiseResult]]赋值'
},()=>{
console.log('reject');
})
console.log(p1);
// 如何获取p1.[[PromiseResult]]的值
// p1.then(res=>{
// console.log(res);
// })
/*
then 的作用是什么?
then 的返回值是什么?
then 中回调函数的作用是社么
then 回调函数的形参作用是什么
then 回到函数中的retrun的作用是什么
*/
原型api-catch
let p = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(123)
// reject('errrr')
},1000)
})
// then 的链式调用
// let p1 = p.then(res=>{ // res=? 123
// return res+1
// }).then(res=>{// res=? 124
// return 'ni'+res
// }).then(res=>{ // res=? ni1234
// return 'hao'+res // 去哪里了?
// })
// // p1 接收的是什么?
// // A haoni124
// // B promise实例
// 问:日志输出顺序是什么
// A 1 2 3 因为都是微任务异步,按照顺序执行
// B 1 2 3 4
// let p1 = p.then(res=>{
// console.log(1);
// }).then(res=>{
// console.log(2);
// }).then(res=>{
// console.log(3);
// }).catch(()=>{
// console.log(4);
// })
let p1 = p.then(res=>{
console.log(1);
}).then(res=>{
console.log(2);
// 1:在then的链式调用用,只要有一个出现问题,后面的都不执行,执行catch
throw new Error('出错了出错了')
}).then(res=>{
console.log(3);
}).catch((err)=>{
// catch作用:1 捕获.then链式调用中的错误
// 2 处理 promis对象 的 已经拒绝的状态
console.log(4);
console.log(err);
})
// catch()
// 返回值是:pending状态的 全新的promise实例对象
console.log(p1);
/*
catch 作用是什么
catch 回调函数作用是什么
catch 回调函数的形参赋值是什么
catch 返回值什么
catch 什么时候使用
可以用户统一处理 promise的已拒绝的状态
*/
原型api-finally
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(123)
}, 1000)
})
let p1 = p.then(res=>{
console.log(1);
}).then(res=>{
console.log(2);
}).then(res=>{
console.log(3);
}).catch((err)=>{
console.log(4);
console.log(err);
}).finally(()=>{
// finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作
console.log('finally');
})
.then(res=>{
console.log('验证finally后是否能够继续then');
})
console.log(p1);
静态api有哪些?4个:resolve()、reject()、all()和race()
1)Promise.resolve()
- 此方法有一个可选的参数,参数的类型会影响它的返回值,具体可分为三种情况(如下所列),其中有两种情况会创建一个新的已处理的Promise实例,还有一种情况会返回这个参数。
(1)当参数为空或非thenable时,返回一个新的状态为fulfilled的Promise。
(2)当参数为thenable时,返回一个新的Promise,而它的状态由自身的then()方法控制,具体细节已在之前的thenable一节做过说明。
(3)当参数为Promise时,将不做修改,直接返回这个Promise。
下面用一个例子演示这三种情况,注意观察Promise的then()方法的第一个回调函数中接收到的决议结果。
let tha = {
then(resolve, reject) {
resolve("thenable");
}
};
//参数为空
Promise.resolve().then(function(value) {
console.log(value); //undefined
});
//参数为非thenable
Promise.resolve("string").then(function(value) {
console.log(value); //"string"
});
//参数为thenable
Promise.resolve(tha).then(function(value) {
console.log(value); //"thenable"
});
//参数为Promise
Promise.resolve(new Promise(function(resolve) {
resolve("Promise");
})).then(function(value) {
console.log(value); //"Promise"
});
2)Promise.reject()
此方法能接收一个参数,表示拒绝理由,它的返回值是一个新的已拒绝的Promise实例。与Promise.resolve()不同,Promise.reject()中所有类型的参数都会原封不动的传递给后续的已拒绝的回调函数,如下代码所示。
Promise.reject("rejected").catch(function (reason) {
console.log(reason); //"rejected"
});
var p = Promise.resolve();
Promise.reject(p).catch(function (reason) {
reason === p; //true
});
第一次调用Promise.reject()的参数是一个字符串,第二次的参数是一个Promise,catch()方法中的回调函数接收到的正是这两个参数。
3)Promise.all()
此方法和接下来要讲解的Promise.race()都可用来监控多个Promise,当它们的状态发生变化时,这两个方法会给出不同的处理方式。
Promise.all()能接收一个可迭代对象,其中可迭代对象中的成员必须是Promise,如果是字符串、thenable等非Promise的值,那么会自动调用Promise.resolve()转换成Promise。Promise.all()的返回值是一个新的Promise实例,当参数中的成员为空时,其状态为fulfilled;而当参数不为空时,其状态由可迭代对象中的成员决定,具体分为两种情况。
(1)当可迭代对象中的所有成员都是已完成的Promise时,新的Promise的状态为fulfilled。而各个成员的决议结果会组成一个数组,传递给后续的已完成的回调函数,如下所示。
var p1 = Promise.resolve(200),
p2 = "fulfilled";
Promise.all([p1, p2]).then(function (value) {
console.log(value); //[200, "fulfilled"]
});
(2)当可迭代对象中的成员有一个是已拒绝的Promise时,新的Promise的状态为rejected。并且只会处理到这个已拒绝的成员,接下来的成员都会被忽略,其决议结果会传递给后续的已拒绝的回调函数,如下所示。
var p1 = Promise.reject("error"),
p2 = "fulfilled";
Promise.all([p1, p2]).catch(function (reason) {
console.log(reason); //"error"
});
4)Promise.race()
此方法和Promise.all()有很多相似的地方,如下所列。
(1)能接收一个可迭代对象。
(2)成员必须是Promise,对于非Promise的值要用Promise.resolve()做转换。
(3)返回值是一个新的Promise实例。
新的Promise实例的状态也与方法的参数有关,当参数的成员为空时,其状态为pending;当参数不为空时,其状态是最先被处理的成员的状态,并且此成员的决议结果会传递给后续相应的回调函数,如下代码所示。
var p1 = new Promise(function(resolve) {
setTimeout(() => {
resolve("fulfilled");
}, 200);
});
var p2 = new Promise(function(resolve, reject) {
setTimeout(() => {
reject("rejected");
}, 100);
});
Promise.race([p1, p2]).catch(function (reason) {
console.log(reason); //"rejected"
});
- 在p1和p2的执行器中都有一个定时器。由于后者的定时器会先执行,因此通过调用Promise.race([p1, p2])得到的Promise实例,其状态和p2的相同,而p2的决议结果会作为拒绝理由被catch()方法中的回调函数接收。
- 根据前面的分析可以得出,Promise.all()能处理一个或多个受监控的Promise,而Promise.race()只能处理其中的一个。
总结promise实例
<!--
总结
1: promise 实例对象创建有几种方式
1.1 new Promise
1.2 then的返回值
1.3 catch 的返回值
1.4 finally 的返回值
1.5 async 函数的返回值
2: 更改promise状态有什么方式
resove() fulfilled 已接受
reject() rejected 已拒绝
3: [[PromiseResult]] 赋值有几种方式
2.1 resolve reject的实参
2.2 then 的return
2.3 async 的return
注意:[[PromiseResult]] 赋值永远不可能是promise实例对象
当[[PromiseResult]] 赋值为 prosmie实例时候。它会赋值该promise实例的.[[PromiseResult]]
4: [[PromiseResult]] 取值有哪些方式
4.1 then的回参
4.2 await 返回值
-->
<script>
// 常见写法
// 1: 自己怎么利用promise进行封装
// 2: 别人写好的怎么用
// function foo() {
// // 创建了promise 对象.
// // 在堆内存中共开辟了一个空间,产生了一个地址
// // return promise对象在堆内存中的地aaabb
// return new Promise((resolve,reject)=>{
// // .... 写一堆待完成代码
// setTimeout(() => {
// resolve('输出运算结果')
// }, 1000);
// })
// }
// // 分析程序:
// // 1:执行哪个函数?
// // 2:返回值是什么?
// // 3:[[PromiseResult]] 赋值什么?
// function bar(){
// let f = foo();
// f.text = '测试 bar 返回值与foo返回值是否为同一个promise'
// console.log(f);
// return f
// // foo return
// // aabb地址
// }
// function fun(){
// let b = bar()
// console.log(b);
// return b
// // bar return
// // aabb地址
// }
// function test(){
// // fun的return aabb地址
// // aabb 指向 foo执行创建的promise对象
// fun().then(res=>{
// console.log(res);
// })
// }
// test()
// 已知 foo bar fun 返回值都是promise实例对象,
// 问题:foo bar fun 返回值是一个promise实例对象 还是 3个promise实例对象
// -----> 一个promise实例,
// 问题 promise.[[PromiseResult]]的赋值是什么
// ----> resolve() 实参 ---> ‘输出运算结果'’
// let p = new Promise()
// let a = p;
// let b = a;
// let c = b;
// ----------------- 深入:promise async综合------------------
function foo() {
return new Promise((resolve,reject)=>{
setTimeout(() => {
resolve('输出运算结果')
}, 1000);
})
}
async function bar(){
let f = foo();
f.text = '测试 bar 返回值与foo返回值是否为同一个promise'
console.log(f);
return f
}
function fun(){
// 结论:bar foo 返回值不是一个promise实例对昂
// bar 返回的promise.[[PromiseResult]] = foo返回的promise.[[PromiseResult]]
let b = bar()
// 运算过程:
// async 输出全新promise实例
// [[PromiseResult]] = async retrun
// [[PromiseResult]] = foo输出的promise实例
// -----> [[PromiseResult]] 禁止赋值promise实例
// [[PromiseResult]] = foo输出的promise实例.[[PromiseResult]]
// [[PromiseResult]] = '输出运算结果'
console.log(b);
return b
}
function test(){
fun().then(res=>{
console.log(res);
})
}
test()
在什么时候使用?
2:promise A+
制定promise的规范。
3:基于原型或者class类如何手写promise。promise的原理是什么?
手写 Promise
// 判断变量否为function
const isFunction = variable => typeof variable === 'function'
// 定义Promise的三种状态常量
const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'
class MyPromise {
constructor (handle) {
if (!isFunction(handle)) {
throw new Error('MyPromise must accept a function as a parameter')
}
// 添加状态
this._status = PENDING
// 添加状态
this._value = undefined
// 添加成功回调函数队列
this._fulfilledQueues = []
// 添加失败回调函数队列
this._rejectedQueues = []
// 执行handle
try {
handle(this._resolve.bind(this), this._reject.bind(this))
} catch (err) {
this._reject(err)
}
}
// 添加resovle时执行的函数
_resolve (val) {
const run = () => {
if (this._status !== PENDING) return
this._status = FULFILLED
// 依次执行成功队列中的函数,并清空队列
const runFulfilled = (value) => {
let cb;
while (cb = this._fulfilledQueues.shift()) {
cb(value)
}
}
// 依次执行失败队列中的函数,并清空队列
const runRejected = (error) => {
let cb;
while (cb = this._rejectedQueues.shift()) {
cb(error)
}
}
/* 如果resolve的参数为Promise对象,则必须等待该Promise对象状态改变后,
当前Promsie的状态才会改变,且状态取决于参数Promsie对象的状态
*/
if (val instanceof MyPromise) {
val.then(value => {
this._value = value
runFulfilled(value)
}, err => {
this._value = err
runRejected(err)
})
} else {
this._value = val
runFulfilled(val)
}
}
// 为了支持同步的Promise,这里采用异步调用
setTimeout(run, 0)
}
// 添加reject时执行的函数
_reject (err) {
if (this._status !== PENDING) return
// 依次执行失败队列中的函数,并清空队列
const run = () => {
this._status = REJECTED
this._value = err
let cb;
while (cb = this._rejectedQueues.shift()) {
cb(err)
}
}
// 为了支持同步的Promise,这里采用异步调用
setTimeout(run, 0)
}
// 添加then方法
then (onFulfilled, onRejected) {
const { _value, _status } = this
// 返回一个新的Promise对象
return new MyPromise((onFulfilledNext, onRejectedNext) => {
// 封装一个成功时执行的函数
let fulfilled = value => {
try {
if (!isFunction(onFulfilled)) {
onFulfilledNext(value)
} else {
let res = onFulfilled(value);
if (res instanceof MyPromise) {
// 如果当前回调函数返回MyPromise对象,必须等待其状态改变后在执行下一个回调
res.then(onFulfilledNext, onRejectedNext)
} else {
//否则会将返回结果直接作为参数,传入下一个then的回调函数,并立即执行下一个then的回调函数
onFulfilledNext(res)
}
}
} catch (err) {
// 如果函数执行出错,新的Promise对象的状态为失败
onRejectedNext(err)
}
}
// 封装一个失败时执行的函数
let rejected = error => {
try {
if (!isFunction(onRejected)) {
onRejectedNext(error)
} else {
let res = onRejected(error);
if (res instanceof MyPromise) {
// 如果当前回调函数返回MyPromise对象,必须等待其状态改变后在执行下一个回调
res.then(onFulfilledNext, onRejectedNext)
} else {
//否则会将返回结果直接作为参数,传入下一个then的回调函数,并立即执行下一个then的回调函数
onFulfilledNext(res)
}
}
} catch (err) {
// 如果函数执行出错,新的Promise对象的状态为失败
onRejectedNext(err)
}
}
switch (_status) {
// 当状态为pending时,将then方法回调函数加入执行队列等待执行
case PENDING:
this._fulfilledQueues.push(fulfilled)
this._rejectedQueues.push(rejected)
break
// 当状态已经改变时,立即执行对应的回调函数
case FULFILLED:
fulfilled(_value)
break
case REJECTED:
rejected(_value)
break
}
})
}
// 添加catch方法
catch (onRejected) {
return this.then(undefined, onRejected)
}
// 添加静态resolve方法
static resolve (value) {
// 如果参数是MyPromise实例,直接返回这个实例
if (value instanceof MyPromise) return value
return new MyPromise(resolve => resolve(value))
}
// 添加静态reject方法
static reject (value) {
return new MyPromise((resolve ,reject) => reject(value))
}
// 添加静态all方法
static all (list) {
return new MyPromise((resolve, reject) => {
/**
* 返回值的集合
*/
let values = []
let count = 0
for (let [i, p] of list.entries()) {
// 数组参数如果不是MyPromise实例,先调用MyPromise.resolve
this.resolve(p).then(res => {
values[i] = res
count++
// 所有状态都变成fulfilled时返回的MyPromise状态就变成fulfilled
if (count === list.length) resolve(values)
}, err => {
// 有一个被rejected时返回的MyPromise状态就变成rejected
reject(err)
})
}
})
}
// 添加静态race方法
static race (list) {
return new MyPromise((resolve, reject) => {
for (let p of list) {
// 只要有一个实例率先改变状态,新的MyPromise的状态就跟着改变
this.resolve(p).then(res => {
resolve(res)
}, err => {
reject(err)
})
}
})
}
finally (cb) {
return this.then(
value => MyPromise.resolve(cb()).then(() => value),
reason => MyPromise.resolve(cb()).then(() => { throw reason })
);
}
}
console.log(MyPromise);