likes
comments
collection
share

从零开始了解Promise(超详细)

作者站长头像
站长
· 阅读数 15

从零开始了解Promise(超详细) 这里引用了一下金色小芝麻博主的图片!!!!

一、Promise简单了解

Promise是JavaScript中的一种异步编程模式,用于处理异步操作和编写更优雅的异步代码。

Promise最早是由CommonJS社区提出的,并在ES6(ECMAScript 2015)规范中正式引入JavaScript语言。它的目的是解决回调地狱(callback hell)问题,即当存在多个嵌套的异步操作时,通过回调函数传递的方式容易导致代码可读性差、难以维护的问题。

Promise的核心概念是代表一个异步操作的对象,该对象可以处于三种状态之一:未完成(pending)、已成功(fulfilled)和已失败(rejected)。当异步操作完成时,Promise对象会从未完成状态转为已成功或已失败状态,并且会执行相应的处理程序。

使用Promise可以通过链式调用的方式编写更加清晰、易于理解的异步代码。它提供了以下几个主要方法:

  1. then(onFulfilled, onRejected):用于注册异步操作成功或失败时的处理程序。
  2. catch(onRejected):用于捕获异步操作失败时的异常。
  3. finally(onFinally):无论异步操作的状态如何,都会执行的处理程序。

Promise通过这些方法的组合使用,可以实现更加复杂的异步操作逻辑,例如并行执行多个异步操作、按顺序执行多个异步操作等。

PromiseES6新增的内置类「内置构造函数」,它基于“承诺者”设计模式,有效管理JS中的异步编程代码,避免回调地狱<br />  
阶段一:Promise全系列基础知识 && async/await的基础知识<br />   
阶段二:Promise异步微任务处理机制 && async/await异步微任务处理机制<br />   
------------<br /> 
页面中的数据大部分都是“动态绑定”的「HTML->DHTML」,而数据一般都是基于ajax/fetch等相关技术从服务器获取的!在这里会涉及一个概念:ajax的并行和串行!!<br />   
ajax并行:可以“同时发送”多个ajax请求「请求之间没有依赖」,哪个请求先完成,则优先处理哪一个!真实项目中,偶尔需要在并行的基础上,扩展一个进阶需求 “等待所有请求都完毕,整体处理啥事情”!<br />  
ajax串行:多个请求之间“存在依赖”,只有等上一个请求成功,下一个请求才可以发送!<br />
异步操作的管理方案:<br />
  1. 回调函数:在异步处理结束后,触发回调函数执行
  $.ajax(…)
   setTimeout(function(){
      ….
   },1000);
   
   这种方案经常会导致回调地狱!!
  1. 基于Promise管理异步操作「以后真实项目中,大部分异步操作,都需要基于Promise管理(或者基于async/await调用执行)」

JQ中基于‘回调函数’的方式,对ajax四部进行了封装 $.ajax(options) options配置项意思

$.ajax{
  	url:'/api/list',
    method:'GET',
    dataType:'json',
    success(value){
      //请求成功时候,触发success回调函数,「失败就出发error回调函数」
      //value就是从服务器获取结果;
    }
}

基于Promise来管理异步编程(封装ajax操作):axios/fetch

const ajax = function ajax(url) {
  return new Promise(resolve => {
    let xhr = new XMLHttpRequest;
    xhr.open('GET', url, true);
    xhr.onreadystatechange = () => {
      if (xhr.readyState === 4 && xhr.status === 200) {
        resolve(JSON.parse(xhr.responseText));
      }
    };
    xhr.send();
  });
};

需求一:

打开一个页面,我们有三部分信息是需要动态绑定「发送三个请求」,三部分信息没有依赖「同时发送」ajax并行:多个请求之间没有依赖,同时发送多个请求,谁先获取到就先处理谁;并行中出现一个特殊的需求:三个请求都成功,做一些自己的事情

let num = 0;
const complete = function complete(){
  
}

$.ajax{
  url:'/api/a',
  success(value){
    console.log('第一个请求成功',value);
  }
}

$.ajax{
  url:'/api/a',
  success(value){
    console.log('第二个请求成功',value);
  }
}

$.ajax{
  url:'/api/a',
  success(value){
    console.log('第三个请求成功',value);
  }
}

基于Promise优化

 let p1 = ajax('/api/a').then(value => {
  console.log('第一个请求成功:', value);
});
let p2 = ajax('/api/b').then(value => {
  console.log('第二个请求成功:', value);
});
let p3 = ajax('/api/c').then(value => {
  console.log('第三个请求成功:', value);
});
Promise.all([p1, p2, p3]).then(values => {
  console.log('请求都成功了:', values); //values存储每一个成功的结果
});

需求二:

我们想获取学生在班级的排名1.首先获取学生的信息 /api/a 一>包含一个id「记录学生的学籍号」2.用获取的信息,获取他的成绩 /api/b?id=学籍号 -> [110,118,98]3.用获取的成绩,在去获取班级排名 /api/c?score=110,118,98 -> 2分析:三个请求之间是有依赖的,只有上一个请求成功,才能基于获取的结果,发送下一个请求;这就是ajax串行如果是基于“回调函数”的方法管理ajax异步请求,在ajax串行操作中,一定会出现“回调中嵌套回调”「回调地狱」从零开始了解Promise(超详细)

基于Promise解决回调地狱

从零开始了解Promise(超详细)

基于Async/Await把异步操作搞成和同步差不多

(async function () {
  let value = await ajax('/api/a');
  let { id } = value;
  value = await ajax('/api/b?id=' + id);
  value = await ajax('/api/c?score=' + value);;
  console.log('该学员的排名是:', value);
})(); 

模拟经典的“回调地狱”

setTimeout(() => {
  console.log('A');

  setTimeout(() => {
    console.log('B');

    setTimeout(() => {
      console.log('C');
    }, 3000);
  }, 2000);
}, 1000); 

基于Promise优化

从零开始了解Promise(超详细)从零开始了解Promise(超详细)

二、 Promise基础知识详细理解

Promise 是ES6新增的内置类;

1.使用方法:

Let p1 = new Promise(executor)

Promise() //Uncaught TypeError: Promise constructor cannot be invoked without'new’ 必须NEW执行,否则报错!!不能作为普通函数执行new Promise() //Uncaught TypeError: Promise reso lver undefined is not a function NEw的时候,必须在Promise中传递executor函数

2.p1是Promise构造函数的实例

1)+ 私有属性/方法

[[Promisestatel]:实例的状态 pending准备「默认」、fulfilled成功、rejected失败[[PromiseResult]]:实例的值 undefined「默认」、成功的结果、失败的原因

2)+公有属性/方法 Promise.prototype

+then+catch+finally+Symbol(Symbol. tostringTag):"Promise"

p1._proto_ -> Promise. prototype -> Object.prototype

展开说明一下:

  1. then(onFulfilled, onRejected):
const promise = new Promise((resolve, reject) => {
  // 异步操作的代码
  resolve('Success');
});

promise.then(result => {
  console.log(result); // 输出: Success
}).catch(error => {
  console.error(error); // 不会执行
});

在上述示例中,通过创建一个Promise对象,并在执行器中调用resolve方法,将Promise状态变为已成功。然后,通过调用then方法注册了一个处理程序,在Promise状态变为已成功时被调用,并输出结果值。

  1. catch(onRejected):
const promise = new Promise((resolve, reject) => {
  // 异步操作的代码
  reject(new Error('Something went wrong.'));
});

promise.then(result => {
  console.log(result); // 不会执行
}).catch(error => {
  console.error(error.message); // 输出: Something went wrong.
});

在上述示例中,通过创建一个Promise对象,并在执行器中调用reject方法,将Promise状态变为已失败,并传递一个错误作为原因。然后,通过调用catch方法注册了一个处理程序,在Promise状态变为已失败时被调用,并捕获并输出错误信息。

  1. finally(onFinally):
const promise = new Promise((resolve, reject) => {
  // 异步操作的代码
  resolve('Success');
});

promise.finally(() => {
  console.log('Cleanup'); // 总是执行
}).then(result => {
  console.log(result); // 输出: Success
});

在上述示例中,通过创建一个Promise对象,并在执行器中调用resolve方法,将Promise状态变为已成功。然后,通过调用finally方法注册了一个处理程序,它无论Promise状态如何都会执行。最后,通过调用then方法注册了一个处理程序,在Promise状态变为已成功时被调用,并输出结果值。

3)把类做为普通对象,设置的静态私有属性 Promise. xxx

+all+any+race+resolve+reject

展开说明一下:

  1. Promise.resolve(value):
const resolvedPromise = Promise.resolve(42);

resolvedPromise.then(result => {
  console.log(result); // 输出: 42
});

在上述示例中,通过Promise.resolve方法创建了一个已经处于"已成功"状态的Promise对象,并将其解析为值42。后续的then方法会接收到该值,并将其打印出来。

  1. Promise.reject(reason):
const rejectedPromise = Promise.reject(new Error('Something went wrong.'));

rejectedPromise.catch(error => {
  console.error(error.message); // 输出: Something went wrong.
});

在上述示例中,通过Promise.reject方法创建了一个已经处于"已失败"状态的Promise对象,并将其拒绝为一个错误原因。后续的catch方法会捕获到该错误,并将错误信息打印出来。

  1. Promise.all(iterable):
const promise1 = Promise.resolve(10);
const promise2 = Promise.resolve(20);
const promise3 = Promise.resolve(30);

const allPromises = Promise.all([promise1, promise2, promise3]);

allPromises.then(results => {
  console.log(results); // 输出: [10, 20, 30]
});

在上述示例中,通过Promise.all方法将多个Promise对象组合在一起,并返回一个新的Promise对象。该新的Promise对象在所有输入的Promise对象都变为已成功状态时,自身也会变为已成功状态,并返回一个包含所有结果值的数组。

  1. Promise.race(iterable):
const promise1 = new Promise(resolve => setTimeout(resolve, 200, 'A'));
const promise2 = new Promise(resolve => setTimeout(resolve, 100, 'B'));

const racePromise = Promise.race([promise1, promise2]);

racePromise.then(result => {
  console.log(result); // 输出: B
});

在上述示例中,通过Promise.race方法将多个Promise对象组合在一起,并返回一个新的Promise对象。该新的Promise对象会响应最先完成的Promise对象,并使用该对象的结果值来解析。

  1. Promise.allSettled(iterable):
const promise1 = Promise.resolve(42);
const promise2 = Promise.reject(new Error('Something went wrong.'));
const promise3 = new Promise(resolve => setTimeout(resolve, 200, 'Done'));

const allSettledPromise = Promise.allSettled([promise1, promise2, promise3]);

allSettledPromise.then(results => {
  console.log(results);
  /*
  输出:
  [
    { status: 'fulfilled', value: 42 },
    { status: 'rejected', reason: Error: Something went wrong. },
    { status: 'fulfilled', value: 'Done' }
  ]
  */
});

在上述示例中,通过Promise.allSettled方法将多个Promise对象组合在一起,并返回一个新的Promise对象。该新的Promise对象会等待所有输入的Promise对象都已完成,不管是已成功还是已失败,然后返回一个包含每个Promise对象状态和结果值/原因的数组。

3.创建实例的5种方法

** 要研究的话题:如何创建实例,以及如何修改其状态和值!**

@1 基于构造函数执行来创建实例

let p1=new Promise(executor);+必须是一个函数+new Promise的时候,会”立即执行"executor函数「同步」+在此函数中,我们一般处理异步编程的代码

+接收Promise内部传递给他的两个实参:resolve / reject
+resolve / reject都是一个小函数作用是修改实例的状态和值<br />
    resolve(value):把实例的状态改为fulfilled,值改为value
    reject(reason):把实例的状态改为rejected,值改为 reason

+一但状态被改为成功或者失败了,则不能再次修改!+如果executor在执行过程中报错,而且此时实例状态还是pending,则把实例的状态改为rejected,值是报错原因「内部捕获了异常信息(try/catch),所以即便报错,对executor外部的代码执行是没有影响的」从零开始了解Promise(超详细)要研究的话题:知道了实例的状态和值,有啥用?1) 知道了实例的状态和值,我们就可以调用”then方法,确定成功和失败各干什么;p. then (onfulfilled, onrejected)+ onfulfilled:在实例状态为成功的时候触发执行+onrejected:在实例状态为失败的时候触发执行+都会把实例的值传递给这两个函数+同一个实例,可以多次调用then方法,最后会把每一次调用传递的onfulfilled/onrejected都执行【不推荐】从零开始了解Promise(超详细)从零开始了解Promise(超详细)**测试 **

Let p1 = new Promise((resolve, reject) =>{
	console. Log(resolve, reject);
});
console. Log (p1);

从零开始了解Promise(超详细)

Let p1 = new Promise((resolve, reject) =>{
	//空
});
console. Log (p1);

从零开始了解Promise(超详细)

Let p1 = new Promise((resolve, reject) =>{
	resolve(100);
});
console. Log (p1);

从零开始了解Promise(超详细)

Let p1 = new Promise((resolve, reject) =>{
	reject(100);
});
console. Log (p1);

从零开始了解Promise(超详细)从零开始了解Promise(超详细)

Let p = new Promise((resolve) =>{});

@2 基于Promise.resolve/reject方法创建实例

+Promise. resolve(10) ->创建一个状态为fulfilled,值是10的实例+Promise. reject(0)->创建一个状态为rejected,值是0的实例

@3 每一次执行then方法,都会返回一个全新的 Promise实例

目的:实现“THEN链”机制(也就是 .then之后还可以再 .then...)

let p2 = p1. then (onfulfilled, onrejected)

  • +p2是一个新的promise实例
  • +p2的状态和值,取决于onfulfilled或者onrejected中的,任意一个方法执行,我们观察执行的细节和结果,来决定p2的状态和值!!
    • +首先看方法执行是否报错;如果执行报错了,则p2就是rejected,值是报错原因!
    • +再看方法执行的返回值,是否是单独的promise实例「别名:PP」
      • +不是:则p2是fulfilled,值就是函数的返回值
      • +是:则p2的状态和值,和PP保持一致!!

从零开始了解Promise(超详细)从零开始了解Promise(超详细)从零开始了解Promise(超详细)

1)THEN链 的穿透机制 catch/finallyp1. then (onfulfilled, onrejected)+如果onfulfilled, onrejected 没有传递,这顺延到下一个THEN “同等状态”要执行的方法+ 原理:如果我们不设置对应的函数,Promise内部会默认设置一个函数,实现 状态/值 的顺延 :::tips 从零开始了解Promise(超详细)默认加了->从零开始了解Promise(超详细) :::

p.catch(onfulfilled) +等价与 p. then (null, onrejected)p.finally(onfulfilled)**+**不论成功还是失败,都会把onfinally执行「一般不用」真实项目开发中,我们使用Promise,then中一般只放onfulfilled「只放成功做的事情」,在THEN链的末尾,设置一个catch,处理失败的情况;根据THEN的穿透机制,中间不论那个环节出现失败的实例,都会顺延至最后一个catch进行失败的统一处理!!从零开始了解Promise(超详细)

@4.Promise.all/any/ race( [promises])

+都会返回一个新实例「别名p,或者称之为总的实例」+p的状态和值,取决于 [promises]实例集合中的,每个实例的状态和值.+ [promises]集合中如果有一项不是promise实例,则要变为:状态为成功,值是本身的实例

+all:所有实例为成功,总实例p才是成功「值:按集合顺序依次存储每一项的值」,如果有一个实例为失败,则总实例p就是失败「值是失败项的值」

:::tips 从零开始了解Promise(超详细)从零开始了解Promise(超详细) ::: +any:兼容性比较差,它是ES靠后版本(11/12)中新增的;只要有一个成功,总实例p就是成功的「值是成功这一项的值」,如果所有项都失败了,则总实例p是失败的,「值是:all promi were rejected」从零开始了解Promise(超详细)+race: 谁先处理完就听谁的,「把最快处理完毕的结果同步给总实例p」

@5.创建promise 实例的第五种方案:

把函数基于async修饰,函数返回值,默认会变成一个promise实例

  • +函数执行如果报错,则返回失败的实例,值是报错原因
  • +不报错,再看返回值
    • +返回的是一个新的promise实例「别名pp」,则pp的状态和值会同步给返回的实例
    • +否则返回的一定是状态为成功,值是返回值的实例

async/await:ES8新增的,Promise「+generator」的语法糖,目的是让Promise用起来更简单

  • +只能作用在函数身上
  • +async
    • 作用1:让函数返回一个promise实例
    • 作用2「主要」:想用await,则所在的函数必须经过async修饰
  • +await promise实例;
    • +如果后面不是promise实例,则默认变为状态为成功,值是本身的实例
    • +“当前上下文”中,遇到await,在await“下面”的代码暂时不能执行,需要等待await“后面”的实例状态为成功,下面代码才可以执行!

// 模拟2秒中从服务器获取数据的操作

const query = function query() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            reject([100, 200, 300]);
        }, 2000);
    });
};

Uncaught (in promise)…….控制台看到这种错误,属于创建了一个失败的promise实例,但是我们并没有对失败的情况做处理,所以出现这个错误「但是这个错误不会杀死主线程(换句话说,不影响其它代码的执行)」await后面的实例如果是失败的,则当前上下文,其下面的代码不会执行「也不赋值给value」;但是以为没做失败的处理,控制台爆红了「不好看」;所以我们期望可以做一下处理..

(async function () {
  await 10; //=> await Promise.resolve(10);
})(); 

从零开始了解Promise(超详细)

(async function () {
  console.log(1);
  try {
    let value = await query();
    console.log(2);
    console.log('成功:', value);
  } catch (_) {
    // await异常捕获「后面实例是失败的」:这里做的事情,相当于promise中的onrejected/catch!
    console.log('失败:', _);
  }
  // 下面的代码会继续执行,类似于promise中finally
  console.log('处理完毕');
})();

从零开始了解Promise(超详细)

三、回调函数的详解

回调函数:把函数做为值,传递给另外一个函数,在另外一个函数执行的某个阶段,可以把我传递的这个函数执行「改变this\传值\接收返回值」 啥时候用回调函数:但凡在某个程序执行到某个阶段,我们想做一些自己的事情,我们都可以基于回调函数,把自己要做的事情传递进去!!// + 一般用于封装公共方法// + 异步处理

$.ajax({
    url:'/api/xxx',
    success(){
        // 这里是我们要干的事情
    }
});
const each = function each(obj, callback) {
    let keys = Reflect.ownKeys(obj);
    keys.forEach(key => {
        callback(obj[key], key);
    });
};
let obj = {
    10: 100,
    x: 200,
    name: '哈哈哈',
    [Symbol()]: '额呵呵'
};
each(obj, (value, key) => {
    // 我要干的事情
    console.log(value, key);
});
setTimeout(()=>{
    // ...
},2000);