Promise&async&fetch

344 阅读13分钟

Promise 笔记

1 Promise 概述

Promise 是异步编程的一种解决方法,比传统的方式更加高效、友好!

使用 Promise 语法需要创建一个 promise 对象,promise 对象中包含一个异步操作, 可以通过实例化 Promise 构造函数来创造 promise 对象。//promise是一构造函数

promise 对象具有三种状态:

  1. pending 状态, 进行中, 刚创建的 promise 对象就处于 pending 状态。
  2. resolved 状态,已成功, 内部的异步操作执行成功,promise 对象的状态由 pending -> resolved
  3. 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 对象,PromiseReusltundefined2. 参数是除了 Promise 对象和 thenable 对象以外的数据类型
   返回一个 resolved 状态的 Promise 对象,PromiseReusltPromise.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 微队列和宏队列

  1. JS 中用来存储待执行回调函数的队列包含2个不同特定的列队

  2. 宏列队:用来保存待执行的宏任务(回调),比如:定时器回调、DOM事件回调、ajax回调。

  3. 微列队:用来保存待执行的微任务(回调),比如:Promise 的回调、MutationObserver 的回调。

  4. JS 执行时会区别这2个队列:

    • (1) JS 引擎首先必须先执行所有的初始化同步任务代码。
    • (2) 每次准备取出第一个宏任务执行前, 都要将所有的微任务一个一个取出来执行。

面试题

#见案例
----------------------------------------------------------------------------------------
MutationObserver 接口提供了监视对 DOM 树所做更改的能力。它被设计为旧的 Mutation Events 功能的替代品,该功能是 DOM3 Events 规范的一部分。

1655298961276

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关键字
​
asyncawait关键字让我们可以用一种更简洁的方式写出#基于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();
        })();

\