Promise 笔记
1 Promise 概述
Promise 是异步编程的一种解决方法,比传统的方式更加高效、友好!
使用 Promise 语法需要创建一个 promise 对象,promise 对象中包含一个异步操作, 可以通过实例化 Promise 构造函数来创造 promise 对象。//promise是一构造函数
promise 对象具有三种状态:
- pending 状态, 进行中, 刚创建的 promise 对象就处于 pending 状态。
- resolved 状态,已成功, 内部的异步操作执行成功,promise 对象的状态由 pending -> resolved
- rejected 状态,已失败。 内部的异步操作执行失败,promise 对象的状态由 pending -> rejected
当 promise 的状态发生改变,就再也不会变了!
Promise 的优势:
#1. 设置回调函数的方式更加灵活
#2. 通过链式调用解决回调地狱(基于then方法返回值也是个promise对象)
2 Promise 基本语法
① 使用 Promise 构造函数创建 Promise 对象
new Promise((resolve, reject) => {})
/*
promise构造函数的参数是一个回调函数,同步执行
js引擎为这个回调函数部署了两个参数,也是函数
resolve('promiseResult')=>状态改变为成功时候,参数会被then方法的第一个回调函数作为参数接收。
*/
1. Promise 构造函数需要一个回调函数作为参数
2. 该回调函数在实例化 Promise 的时候会被自动调用,是同步执行的
② 修改 promise 对象的状态
// 改变 Promise 对象的状态
const p4 = new Promise((resolve, reject) => {
// 修改为 resolve 状态, 可以通过参数传递结果
resolve('OK,Success');
// 修改为失败状态, 同时设置 promiseResult
// reject('error');
reject({errorno: 10012, errorinfo:'sorry,you are error'});
});
1. Promise 构造函数的回调函数,会接收到两个参数,两个参数都是函数类型
2. 调用第一个参数,可以将 Promise 对象的状态改为 resolved; 调用第二个参数可以将 Promise 对象的状态改为 rejected。
3. Promise 对象的状态一旦改变,就永远定格为该状态,不会再改变
③ 为 promise 对象设置回调函数
// 创建 Promise 对象
const p = new Promise((resolve, reject) => {
// 设置为成功状态
// resolve('OK');
// 设置为失败状态
reject('error');
});
// 给 Promise 对象设置回调函数
p.then(result => {
console.log('成功了', result);
}, result => {
console.log('失败了', result);
});
1. Promise 对象具有 then 方法,该方法可以设置两个回调函数作为参数; 两个回调函数可以在 Promise 对象状态发生改变的时候自动调用 -------//(状态一旦改变then方法进入微掉队列中等待执行)
2. 如果 Promise 对象状态变为 resolved,指定第一个回调函数。
2. 如果 Promise 对象状态变为 rejected,指定第二个回调函数。
3 Promise 实例的方法
3.1 then 方法
① 参数
1. 第一个参数: Promise 对象状态变为 resolved(成功) 时所调用的回调函数
2. 第二个参数: Promise 对象状态变为 rejected(失败) 时所调用的回调函数
② 返回值
then() 方法的返回值一定是一个 Promise 对象,该 Promise 对象的状态取决于 then() 方法回调函数的返回值(then 可以设置两个回调函数,哪个回调函数执行就取决于谁)****
then() 方法回调函数的返回值对 then() 方法返回的 Promise 对象的影响,如下:
1. 第一种情况 没有返回值; then() 方法返回的 Promise 对象的状态是 resolved(成功), PromiseResult 是 undefined
2. 第二种情况 返回 Promise 对象以外的数据类型; then() 方法返回的 Promise 对象的状态是 resolved(成功), PromiseResult 是该返回值
3. 第三种情况 返回 Promise 对象; then()方法的返回值就是该 Promise 对象。//相当于new了一个新对象 状态 状态码设置好
4. 第四种情况 该回调函数中出现错误(非语法错误); then() 方法返回的 Promise 对象的状态是 rejected(失败), PromiseResult 是错误详情
#myself总结:then方法的回调 主要决定then方法返回对象的 状态和状态码(就像创建时候的resolve('描述')),毕竟一个promise对象就要设置这些属性,所以各种情况都会围绕其展开
3.2 catch 方法
① 参数
需要一个回调函数作为参数,如果 Promise 对象的状态是 rejected,就会执行该回调函数
② then 和 catch 可以配合使用
promise对象
.then(() => {
// 状态 resolved,执行该回调
})
.catch(() => {
// 状态 rejected,执行该回调
})
//自己加的
promise对象
.then(() => {
// 状态 resolved,执行该回调
}) //若只写成功的回调 失败的请求就处理不了的 理论上你可以只写一个回调
③返回值
catch() 方法返回值也是 Promise 对象,Promise 对象的状态由回调函数的返回值决定; 规则同 then 方法完全一致,四种情况// 一般不会在catch后面继续调用
④异常穿透
promsie对象
.then(() => {})
.then(() => {})
.then(() => {})
.then(() => {})
.then(() => {})
.catch(() => {})
#链式调用中,then() 方法只处理 resolved 状态的回调函数,最后由 catch() 的回调函数处理 rejected 状态的 Promise
//若遇到reject的情况 处理不了就原路返回promise,然后一步步依次到最后的catch,不是直接跳下去的
3.3 finally
1. 与 then()、catch() 方法用法相似,设置回调涵数作为参数。
2. 只要 Promise 对象状态改变, finally 中的回调函数就会执行,不论 Promise 对象的状态是 resolved 还是 rejected //他的回调函数没有参数
//像ajax 的loadend?不管怎样最后都要执行的
4 Promise 构造函数本身的方法
4.1 Promise.resolve()
① 功能
该方法可以返回一个 Promise 对象,用于快速创建一个 成功状态 的Promise 对象。
② 根据参数不同返回的 Promise 对象的状态也不同:
1. 没有参数
返回一个 resolved 状态的 Promise 对象,PromiseReuslt 为 undefined
2. 参数是除了 Promise 对象和 thenable 对象以外的数据类型
返回一个 resolved 状态的 Promise 对象,PromiseReuslt 为 Promise.resolve() 的参数
3. 参数是一个 Promise 对象
返回的就是这个 Promise 对象
4. 参数是 thenable 对象,设置了 then 方法且按照规范设置,称为 thenable 对象
返回的 Promise 对象状态取决于 thenable 对象的 then() 方法中调用了第一参数还是第二个参数
// 4. 参数是一个 thenable 对象(具有 then 方法对象)
const obj = {
//对象里按贵方then方法
then(resolve, reject) {
const rand = Math.floor(Math.random() * 10);
if (rand >= 5) {
resolve(rand);
} else {
reject(rand);
}
}
}
const p4 = Promise.resolve(obj);
p4
.then(res => {
console.log('p4 成功!', res);
})
.catch(res => {
console.log('p4 失败!', res);
});
4.2 Promise.reject()
1. 该方法返回一个 rejected 状态的 promise 对象
#2. 该方法所有的参数都被视为 promise 对象的 PromiseResult
4.3 Promise.all()
1. Promsie.all() 的参数
需要一个可遍历对象作为参数,要求可遍历对象中的每个成员都是 Promise 对象;
如果可遍历对象中有成员不是 Promise 对象,系统会使用 Promise.resolve() 方法将该成员作为参数返回 Promise 对象
// 成功! ['张三', 10098, {…}]
2. Promsie.all() 返回一个 Promise 对象,该 Promise 对象的状态由参数中的成员决定:
① 如果参数中的成员最终状态都变为 resolved,返回的 Promise 对象状态也是 resolved; PromiseResult 是个数组,数组中包含参数成员的 PromiseResult
② 如果参数成员中任何一个状态改为 rejected,返回的 promise 对象状态就是 rejected, PromiseResult 是参数成员中第一个变为 rejected 的成员的 PromiseResult
#3. Promise.all() 的参数如果不是可遍历对象,返回一个 rejected 状态的 Promise 对象,promiseresoult 就是报错信息
------------------------------------------------------------------------------------- -//promise.all()应用
//ajax(),是promise封装的发送ajax请求的函数,返回promise对象
ajax({ url: ' https://music.cyrilstudio.top/top/mv?limit=5' })
.then(res => {
return Promise.all(res.data.map(item => ajax({ url: 'https://music.cyrilstudio.top/mv/detail?mvid=' + item.id })));
})
.then(res => {
console.log(res);
})
.catch(err => {
console.log('请求失败!', err);
});
都成功才成功,一个失败最终失败!
4.4 Promise.race()
1. Promise.race() 方法同 Promise.all() 一样,将多个 Promise 实例,包装成一个新的 Promise 实例。
2. 参数也要求是个可遍历对象,可遍历对象的成员要求是 Promise 对象, 不是 Promise 对象,会用 Promise.resolve() 方法变为 Promise 对象
3. 一旦可遍历对象中的某个 Promise 实例状态率先变为 resolved 或 rejected,返回的 Promise 实例就会跟着变,那个率先改变的 Promise 实例的返回值,就是最终的返回值。
4.5Promise.allsettled()
const p = Promise.allSettled([p1, p2]);
p.then(res => {
console.log('p成功', res);
}, reason => {
console.log('p失败', reason);
})
//返回一个 promise,该 promise 在所有 promise 都敲定后完成,并兑现一个对象数组,其中的对象对应每个 promise 的结果 状态和promiseResult
5 微队列和宏队列
-
JS 中用来存储待执行回调函数的队列包含2个不同特定的列队
-
宏列队:用来保存待执行的宏任务(回调),比如:定时器回调、DOM事件回调、ajax回调。
-
微列队:用来保存待执行的微任务(回调),比如:Promise 的回调、MutationObserver 的回调。
-
JS 执行时会区别这2个队列:
- (1) JS 引擎首先必须先执行所有的初始化同步任务代码。
- (2) 每次准备取出第一个宏任务执行前, 都要将所有的微任务一个一个取出来执行。
面试题
#见案例
----------------------------------------------------------------------------------------
MutationObserver 接口提供了监视对 DOM 树所做更改的能力。它被设计为旧的 Mutation Events 功能的替代品,该功能是 DOM3 Events 规范的一部分。
6 node使用promise
// 导入支持 Promise 的 fs 模块
const fs = require('fs/promises')
// 按顺序读取文件
fs.readFile('./data1.txt')
.then(data => {
console.log('data1:\n' + data.toString() + '\n\n');
return fs.readFile('./data2.txt');
})
.then(data => {
console.log('data2:\n' + data.toString() + '\n\n');
return fs.readFile('./data3.txt');
})
.then(data => {
console.log('data3:\n' + data.toString() + '\n\n');
})
.catch (err => {
console.log('读取失败!', err);
})
7 async函数
7.1asnc函数:
async 函数是使用async关键字声明的函数。
async 函数是AsyncFunction构造函数的实例, 并且其中允许使用await关键字
async和await关键字让我们可以用一种更简洁的方式写出#基于Promise的异步行为,而无需刻意地链式调用promise。
7.2async函数定义方式:
在函数声明语句的前面加上
async关键字,就变成了 async 函数。
// 1 直接定义
async function fn01() { }
// 2 将匿名的 async 函数赋值
var fn02 = async function () { };
// 3 箭头函数
var fn03 = async () => { };
// 4 对象中的方法
var obj = {
m1: async function () { },
m2: async () => { },
async m3() { }
};
// 5 回调函数
[].map(async () => { });
// 6 立即执行函数
(async function () { })();
7.3 async 函数的返回值的四种情况
#同promise的then方法的回调函数返回值规则一样
// 情况一 函数体内没有 return , async 函数返回的 Promise 对象状态是 resolved, result 是 undefined
async function func01() {
console.log('func01');
}
const p1 = func01();
console.log(p1); // {<fulfilled>: undefined}
// 情况二 函数体内return 的是非 Promise 对象的数据 , async 函数返回的 Promise 对象状态是 resolved, result 是 return 右边的值
async function func02() {
console.log('func02');
return [10, 20, 30, 40];
}
const p2 = func02();
console.log(p2); // {<fulfilled>: [10,20,30,40]}
// 情况三 函数体内return的是一个Promise对象,async函数的返回值就是该Promise对象
async function func03() {
return new Promise((resolve, reject) => {
//reject('hello啊');
resolve('hello啊');
})
}
const p3 = func03();
console.log(p3); // {<fulfilled>: hello啊}
// 情况四 函数体内有错误,返回一个 rejected 状态的 Promise 对象,result 是错误信息
async function func04() {
console.log(username);//非语法错误
}
const p4 = func04();
console.log(p4); // {<rejected>: ReferenceError: username is not defined}
7.4 await表达式
async 函数名(){
await 表达式;
await 表达式;
await 表达式;
}
#注意点
1. await 关键字与右侧的表达式共同组成一个 await 表达式
2. await 表达式必须写在 async 函数的中; async 函数中可以有 0 个或多个 await 表达式。
3. await 表达式可以取到 Promise 对象的 result,必须等到 Promise 实例的状态发生变化,await 表达式才能取到值。
#await之前的代码是同步的 之后的代码异步
7.5 await表达式的值
// 情况一 最常见的情况,右侧表达式是个 Promsie 对象, await 表达式的返回值是该Promsie对象的 result
(async function () {
const res = await Promise.resolve('hello,promise');
console.log(res);
})();
// 情况二 如果右侧是个其他类型的数据(非 Promise 类型),await 表达式的值就是这个数据。
(async function () {
const res = await [100, 200, 300, 400]
console.log(res);
})();
情况三 如果右侧是一个 thenable 对象(即定义`then`方法的对象),那么会将其等同于 Promise 对象。
(async function () {
const res = await {
then(resolve, reject) {
resolve('hello,安妮');
}
}
console.log(res);
})();
/*
await 表达式的值由 await 关键字右侧的表达式决定:
1. 一般右侧会是 Promise 对象(或能得到 Promise 对象的表达式),await 表达式的值就是该 Promise 对象的 Result。
2. 如果右侧是个其他类型的数据(非 Promise 类型),await 表达式的值就是这个数据。
3. 如果右侧是一个 thenable 对象(即定义`then`方法的对象),那么会将其等同于 Promise 对象。
*/
7.5 await 处理 rejected 状态的 Promise 对象
如果 await 右侧是个状态为 rejected 的 Promise 对象,await 默认不会处理,会报错。我们建议将 await 表达式放在try...catch结构里面。
async function f() {
try {
await Promise.reject('出错了');
} catch(e) {
//这里e是失败状态的result
}
}
7.6async 与 await 实现链式调用
(async function(){
try {
let data1 = await readFile('../content1.txt');
let data2 = await readFile('../content2.txt');
let data3 = await readFile('../content3.txt');
console.log(data1, data2, data3)
} catch (err) {
console.log(err);
}
})();
//按顺序读取文件 await之前的代码同步 之后的异步 其实就是同步的样子去执行异步代码
7.6 总结:
async/await 可以直接拿到 Promise 的结果,可以代替 then() 方法和回调函数。可以取代链式调用,是回调地狱的终极解决方案。
缺点是状态为 rejected 的 Promise 实例,会抛出异常,所以需要写在 try...catch结构中防止出错。
8 fetch
Fetch 被设计用来取代 XMLHttpRequest,它提供了许多与 XMLHttpRequest 相同的功能,但被设计成更具可扩展性和高效性,他和XMLHttpRequest都是浏览器提供的一个web接口
8.1 fetch 方法返回一个 Promise 对象
fetch(url, {
method: 'POST', // *GET, POST, PUT, DELETE
headers: {
'Content-Type': 'application/json'
// 'Content-Type': 'application/x-www-form-urlencoded',
//urlencoded请求类型 key1=val1&key2=val2 的方式进行编码
},
body: JSON.stringify(data) // body data type must match "Content-Type" header
});
#两个参数,参数一请求地址,参数二 请求报文属性配置对象,按需求看是否使用默认配置
8.2 fetch使用
#01 fetch链式调用
fetch('https://music.cyrilstudio.top/toplist')//返回的对象一般都是成功状态,result值就是响应报文
/*
为什么要用两次then?
首先fetch返回的是promise对象,res
若状态为成功并且result为res ,会首先拿到整个响应报文,而我们要的是res.body响应体里的内容
但是响应体里是个可读流(以前浏览器不支持的 node支持,现在也支持了)
但是,res对象有2个方法。res.text():把响应体处理成字符串,响应体内容是字符串就处理成字符串
res.json():把json格式的字符串处理为对象,一般响应内容都是json字符串
但是,返回值也是promise对象,状态就是对应的状态,result就是响应体的数据,(读取流全部读完)
所以,可以将他return 出来 ,然后可以再用then方法获取内容
*/
.then(res => res.json())
.then(data => {
console.log(data) //此时拿到后端给的数据
})
-----------------------------------------------------------------------------------------
#02 fetch与await链式调用
(async function () {
try {
// 获取所有的榜单
const res1 = await fetch('https://music.cyrilstudio.top/toplist')
//将响应体内容以对象的形式得到
const data1 = await res1.json()
// 获取第一榜单中的所有的歌曲
const res2 = await fetch('https://music.cyrilstudio.top/playlist/detail?id=' + data1.list[0].id);
const data2 = await res2.json();
})();
\