JavaScript 期约 Promise 总结

news2024/11/23 16:28:50

同步与异步的概念

JavaScript 是一门单线程的语言,这意味着它在任何给定的时间只能执行一个任务。

然而,JavaScript 通过异步编程技术来处理并发操作,以避免阻塞主线程的情况。

在这里插入图片描述

在上图中,同步行为的进程 A 因为等待进程 B 执行完而被阻塞了一段时间。异步行为的进程 A 则会继续执行,等到进程 B 有了结果,它再告知进程 A 来处理。

异步行为是为了优化计算量大而耗时长的操作,但也并非只能处理该类情况,只要需要执行某个异步操作且不想主线程被阻塞,那么都可以使用异步编程。异步行为类似于系统中断。

同步行为对应内存中顺序执行的处理器指令,指令执行完后就容易推断出程序的状态,每个操作都是可预测性的。

设计一个这样的异步系统是困难的,因为你不知道异步结果什么时候可以获取。

JavaScript 最初的异步编程方式:回调

使用回调作为异步编程的方式:回调函数作为另一个函数的参数,并在某个事件发送或异步操作完成后执行。

function fetchData(callback){
	setTimeout(function(){
		callback('Data fetched');
	},1000);
}

fetchData(function(result){
	console.log(result);
});

从宏任务(macrotask)和微任务(microtask)的观点来看这段代码,我们可以将其分为以下几个步骤:

  1. 宏任务1:开始执行主程序,调用fetchData函数。
  2. 宏任务2fetchData函数中的setTimeout会将回调函数注册为一个宏任务,该宏任务将在1秒后执行。
  3. 宏任务3:主程序继续执行,没有其他宏任务,因此等待。
  4. 微任务1:当宏任务2(setTimeout的回调)执行时,它调用传递给它的回调函数,并将其视为微任务。这个微任务将立即执行,因为它没有等待。
  5. 微任务2:微任务1执行完成后,主程序没有其他宏任务要执行,但是会检查是否有待处理的微任务。在这种情况下,微任务2是fetchData函数调用中的回调函数中的console.log(result)语句。

加上对失败回调的处理:

function fetchData(successCallback, errorCallback) {
  setTimeout(function () {
    // 模拟一个错误,你可以根据具体情况处理错误
    const error = null; // 这里假设没有错误
    if (error) {
      errorCallback(error); // 调用失败回调函数并传递错误
    } else {
      successCallback('Data fetched'); // 调用成功回调函数并传递数据
    }
  }, 1000);
}

// 使用 fetchData 函数
fetchData(
  function (data) {
    console.log('Success:', data);
  },
  function (error) {
    console.error('Error:', error);
  }
);

这种模式有很多弊端:首先需要在指定时间内才能得到异步函数的返回值,其次需要提前定义好回调函数。

最后,多个异步操作嵌套在一起,会形成回调地狱

function fetchData(callback) {
  setTimeout(function () {
    callback('Data fetched');
  }, 1000);
}

function processData(data, successCallback, errorCallback) {
  setTimeout(function () {
    // 模拟一个错误
    const error = null; // 这里假设没有错误
    if (error) {
      errorCallback(error);
    } else {
      successCallback('Data processed');
    }
  }, 1000);
}

function saveData(data, successCallback, errorCallback) {
  setTimeout(function () {
    // 模拟一个错误
    const error = new Error('Save failed');
    errorCallback(error);
  }, 1000);
}

fetchData(function (data) {
  processData(
    data,
    function (processedData) {
      saveData(
        processedData,
        function () {
          console.log('Data saved successfully');
        },
        function (error) {
          console.error('Error saving data:', error);
        }
      );
    },
    function (error) {
      console.error('Error processing data:', error);
    }
  );
});

在这里插入图片描述

嵌套的回调难以阅读和维护。

新时代:期约-Promise

期约是对尚不存在结果的一个替身。

期约提供了一种更清晰和可维护的方式来处理异步操作,避免了回调地狱的问题。

期约是基于 Promises/A+ 规范建立的。

期约的状态

Promise 是 ECMAScript6 新增的引用类型,是一个具有状态的对象。

它有如下三种状态:

  1. 待定-pending。期约最初始的状态。
  2. 兑现-fullfilled。也可以称为 解决-resolved
  3. 拒绝-rejected

在这里插入图片描述

状态一经改变,不可修改。

期约的状态是私有的,只能在内部进行操作,不能被外部代码检测和修改。

初始化期约-new Promise(executor)

使用 XMLHttpRequest 模拟异步操作创建期约:

// 建立请求,创建期约的工厂函数
function makeRequest(url){
	return new Promise((resolve,reject)=>{
		// 异步操作
		const xhr = new XMLHttpRequest();
		xhr.open('GET', url);
		xhr.onload = function () {
	      if (xhr.status >= 200 && xhr.status < 300) {
	        // 请求成功,将响应文本作为成功结果
	        resolve(xhr.responseText);
	      } else {
	        // 请求失败,将错误信息作为失败原因
	        reject('请求失败,状态码: ' + xhr.status);
	      }
	    };
	    xhr.onerror = function () {
	      // 请求错误,将错误信息作为失败原因
	      reject('网络错误');
	    };
	    xhr.send();
	});
}

const myPromise=makeRequest('https://example.com/api/data');

使用 new 创建 Promise 实例时,需要传入一个执行器(executor)函数作为参数,该函数接受两个参数:resolvereject

前面提到期约的状态只能在内部操作,这个操作就是在执行器函数中完成的。

resolve 会将 Promise 状态切换为 fullfilledreject则会将其切换为 rejected。同时,调用 reject 会抛出错误。

执行器函数是期约的初始化程序,且是同步执行的,当初始化期约时就已经改变了期约的状态。

期约的构造器方法|静态方法之二

Promise.resolve()

Promise.resolve 是一个静态方法,它返回一个已解决(fulfilled)的 Promise 对象,并可以选择将一个值解析为成功的结果。

如果传递给 Promise.resolve 的值本身已经是一个 Promise 对象,则它会保持不变(不会再次解析)。

setTimeout(console.log, 0, Promise.resolve());
setTimeout(console.log, 0, Promise.resolve(1));

const p = new Promise(()=>{});
setTimeout(console.log, 0, Promise.resolve(p));
setTimeout(console.log, 0, p === Promise.resolve(p));

在这里插入图片描述

Promise.reject()

Promise.reject 是一个静态方法,用于创建一个已拒绝(rejected)的 Promise 对象,并指定一个原因(通常是一个错误对象)作为拒绝的原因。

Promise.resolve 不同,Promise.reject 不会解析传递给它的值,而是将其作为拒绝原因直接传递给 Promise 对象。

setTimeout(console.log, 0, Promise.reject());
setTimeout(console.log, 0, Promise.reject(1));

const p = new Promise(()=>{});
setTimeout(console.log, 0, Promise.reject(p));
setTimeout(console.log, 0, p === Promise.reject(p));

在这里插入图片描述

注意到,错误被抛出但没有被捕获(Uncaught)。我们给它套上 try...catch 试试。

try {
    setTimeout(console.log, 0, Promise.reject());
} catch (e) {
    console.log(e);
}

在这里插入图片描述

这就奇怪了,为什么还是没有捕获到错误呢?

因为 try..catch 只能捕获同步代码中的错误,它位于当前执行栈中,而 Promise.reject 会被推入微任务队列,当当前执行栈执行完后,再执行它。

要和异步代码交互,只能使用期约的实例方法——Promise.prototype.thenPromise.prototype.catchPromise.prototype.finally

期约的实例方法

期约的实例方法是连接外部同步代码和内部异步代码的桥梁。

任何暴露的异步结构——或者叫做期约的实例方法中都实现了一个 then() 方法。这个方法被认为实现了一个 Thenable 接口。

Promise.prototype.then

Promise.prototype.then 是 Promise 对象的一个实例方法,用于附加回调函数来处理 Promise 的解决(fulfilled)和拒绝(rejected)状态。

promise.then(onFulfilled, onRejected)

then 方法返回一个新的 Promise 对象,该对象有以下几种情况:

  1. 如果 onFulfilledonRejected 返回一个值(不是 Promise),则返回的新 Promise 将以该值解决。
  2. 如果 onFulfilledonRejected 抛出异常,则返回的新 Promise 将以该异常作为原因拒绝。注意返回错误对象会把该错误对象包装在一个解决的期约中。
  3. 如果 onFulfilledonRejected 返回一个 Promise,则返回的新 Promise 将与该返回的 Promise 具有相同的状态和结果。

下面是一个示例:

const promise = new Promise((resolve, reject) => {
  // 模拟异步操作
  setTimeout(() => {
    const randomNumber = Math.random();
    if (randomNumber < 0.5) {
      resolve(`成功:${randomNumber}`);
    } else {
      reject(`失败:${randomNumber}`);
    }
  }, 1000);
});

promise.then(
  (result) => {
    console.log(`成功回调:${result}`);
  },
  (error) => {
    console.error(`失败回调:${error}`);
  }
);

then 方法返回一个新的 Promise 对象,那么就可以链式调用它。

// 模拟延迟
function delay(ms) {
  return new Promise((resolve) => {
    setTimeout(resolve, ms);
  });
}

function fetchUserData() {
  return delay(1000).then(() => {
    return { username: "john_doe", email: "john@example.com" };
  });
}

function fetchUserPosts(username) {
  return delay(1000).then(() => {
    return ["Post 1", "Post 2", "Post 3"];
  });
}

function displayUser(username, posts) {
  console.log(`Username: ${username}`);
  console.log("Posts:");
  posts.forEach((post, index) => {
    console.log(`${index + 1}. ${post}`);
  });
}

fetchUserData()
  .then((user) => {
    console.log("Fetching user data...");
    console.log(user);
    return fetchUserPosts(user.username);
  })
  .then((posts) => {
    console.log("Fetching user posts...");
    console.log(posts);
    displayUser("john_doe", posts);
  })
  .catch((error) => {
    console.error("Error:", error);
  });

输出:

Fetching user data...
{ username: 'john_doe', email: 'john@example.com' }
Fetching user posts...
[ 'Post 1', 'Post 2', 'Post 3' ]
Username: john_doe
Posts:
1. Post 1
2. Post 2
3. Post 3

then 的链式调用避免了回调地狱,提高了代码的可维护性。

Promise.prototype.catch

虽然 then 方法已经可以为期约添加拒绝处理程序——promise.then(null,onRejected),但是这样不是很美观,于是就有了语法糖:promise.catch(onRejected)

行为上与 then 是一致的。这里不再细究。

Promise.prototype.finally

finally 方法用于给程序添加 onFinally 回调函数,该回调无论 Promise 对象是 fullfilled 还是 rejected 都会执行。

const promise = new Promise((resolve, reject) => {
  // 模拟异步操作
  setTimeout(() => {
    const randomNumber = Math.random();
    if (randomNumber < 0.5) {
      resolve(`成功:${randomNumber}`);
    } else {
      reject(`失败:${randomNumber}`);
    }
  }, 1000);
});

promise
  .then(
    (result) => {
      console.log(`成功回调:${result}`);
    },
    (error) => {
      console.error(`失败回调:${error}`);
    }
  )
  .finally(() => {
    console.log("不管成功或失败,都会执行这里的回调");
  });

大多数情况下,调用 finally 会原样返回期约。

如果期约是待定的且在 onFinally 处理程序中抛出错误或返回了一个拒绝期约,则会返回一个拒绝期约。

这个方法避免了 thencatchonFufilledonRejected 中出现重复冗余代码。

期约的非重入-non-reentrancy特性

当一个期约进入"落定状态"(settled state)时,与该状态相关的处理程序(例如,.then.catch 中的回调函数)不会立即执行,而是会被排入执行队列,等待事件循环处理。

一旦Promise对象进入了已成功或已失败状态,它就被认为是"落定",不再处于悬挂状态。在这个状态下,Promise的结果或错误已经确定,不会再发生变化。

与附加处理程序相关的同步代码(即添加处理程序的代码之后的代码)会在处理程序执行之前优先执行。

这个特性的存在是为了确保期约处理程序的执行不会中断当前执行的同步代码块。

Promise.resolve().then(() => console.log("onResolved 执行"));
console.log("同步代码执行");

输出

同步代码执行
onResolved 执行

更为精彩的示例:

// 创建一个Promise
const myPromise = new Promise((resolve, reject) => {
    console.log("Promise开始执行");
    // 模拟异步操作
    setTimeout(() => {
        resolve("成功"); // 期约进入落定状态
    }, 2000);
    console.log("resolve() 返回");
});

console.log("Promise创建完成");

// 添加处理程序
myPromise.then((result) => {
    console.log(`处理程序执行,结果为:${result}`);
});

console.log("处理程序添加完成");

// 同步代码
console.log("同步代码执行");

输出

Promise开始执行
resolve() 返回
Promise创建完成
处理程序添加完成
同步代码执行
处理程序执行,结果为:成功

哪怕你把期约状态变化的代码的封装放在添加处理程序之后,结果也是一样:

let synchronousResolve;
// 创建一个Promise
const myPromise = new Promise((resolve, reject) => {
    synchronousResolve = function () {
        console.log("Promise开始执行");
        // 模拟异步操作
        setTimeout(() => {
            resolve("成功"); // 期约进入落定状态
        }, 2000);
        console.log("resolve() 返回");
    }
});

console.log("Promise创建完成");

// 添加处理程序
myPromise.then((result) => {
    console.log(`处理程序执行,结果为:${result}`);
});

synchronousResolve();

console.log("处理程序添加完成");

// 同步代码
console.log("同步代码执行");

邻近处理程序的执行顺序

当给一个期约(Promise)添加多个处理程序(例如,.then().catch().finally()),这些处理程序会按照它们被添加的顺序依次执行。

const myPromise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("成功");
  }, 2000);
});

myPromise
  .then((result) => {
    console.log(`第一个处理程序执行,结果为:${result}`);
  })
  .then(() => {
    console.log("第二个处理程序执行");
  })
  .catch((error) => {
    console.error(`捕获到错误:${error}`);
  })
  .finally(() => {
    console.log("无论如何都会执行的finally处理程序");
  });

输出

第一个处理程序执行,结果为:成功
第二个处理程序执行
无论如何都会执行的finally处理程序

传递解决值和拒绝理由

当一个Promise对象进入"已成功"或"已失败"的落定状态后,它会提供相应的解决值(如果成功)或拒绝理由(如果失败)给与之相关联的处理程序。这些解决值和拒绝理由会作为函数参数传递给处理程序,从而允许进一步操作这些值。

  1. 当一个Promise成功(通过调用resolve())时,解决值会传递给.then()处理程序,允许您进一步操作这个值。
  2. 当一个Promise失败(通过调用reject())时,拒绝理由会传递给.catch()处理程序,允许您处理错误情况。
  3. 在执行Promise的执行器函数中,解决值和拒绝理由是作为resolve()reject()函数的第一个参数传递的。
  4. Promise.resolve()Promise.reject()方法在被调用时也可以接收解决值和拒绝理由,并返回一个已经处于相应状态的Promise对象。这些值将会传递给与之关联的处理程序。
function fetchDataFromServer(url) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            const success = Math.random() < 0.7;
            if (success) {
                const data = { id: 1, name: "John Doe" };
                resolve(data); // 请求成功,传递数据
            } else {
                reject("网络请求失败"); // 请求失败,传递错误信息
            }
        }, 1000);
    });
}

fetchDataFromServer("https://example.com/api/data")
    .then((response) => {
        console.log("成功获取数据:", response);
        // 在这里可以对获取到的数据进行操作
        return response.name; // 返回一个新值
    })
    .catch((error) => {
        console.error("网络请求错误:", error);
        // 在这里可以处理网络请求失败的情况
        throw new Error("处理失败"); //不会阻止代码执行
    })
    .then((value) => {
        console.log("在第二个.then()中获取的值:", value);
    })
    .catch((error) => {
        console.error("在第二个.catch()中获取的错误:", error.message);
    });

console.log("网络请求已发出");

在这里插入图片描述

在这里插入图片描述

期约连锁形成 Promise 链

前文提到过 then 的链式调用,也被称为期约连锁thencatchfinally 都返回一个新期约对象,链式调用它们就会形成一条期约链

function delay(ms, str) {
    return new Promise((resolve) => {
        console.log(str);
        setTimeout(resolve, ms);
    });
}
delay(1000, "promise 1")
    .then(() => delay(1000, "promise 2"))
    .then(() => delay(1000, "promise 3"))
    .then(() => delay(1000, "promise 4"));

在这里插入图片描述

期约图的概念

一个期约可能对应多个处理程序。每个处理程序也可能返回一个新期约。

这就可能形成期约图。

期约图中,一个期约是一个节点,期约的处理程序则是对应期约的不同边。

let A = new Promise((resolve, reject) => {
    console.log("A");
    resolve();
});
let B = A.then(() => console.log('B'));
let C = A.then(() => console.log('C'));
B.then(() => console.log('D'));
B.then(() => console.log('E'));
C.then(() => console.log('F'));
C.then(() => console.log('G'));

如上代码形成如下有向非循环的期约图:

在这里插入图片描述

期约合成的静态方法

期约合成是指将多个期约组合成一个期约。

Promise.all

Promise.all 方法创建的期约会在一组期约全部解决后再解决,即并行执行多个异步操作,并在所有操作都成功完成时才返回结果,如果其中任何一个失败,Promise.all 方法立即返回失败的期约。

Promise.all(iterable);

Promise.all 接受一个可迭代对象作为参数。可迭代对象的元素会通过 Promise.resolve 转换为期约

function createPromise() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            const randomnNumber = Math.random();
            (randomnNumber < 0.5 && resolve(`成功: ${randomnNumber}`))
                || reject(`失败: ${randomnNumber}`);
        },1000);
    });
}
let p = Promise.all([
    createPromise(),
    createPromise()
]);
setTimeout(console.log, 0, p);
p.then(() => setTimeout(console.log, 0, 'all() resolved'));

Promise.race

Promise.all 一样,Promise.race 接受一个可迭代对象作为参数。可迭代对象的元素会通过 Promise.resolve 转换为期约

不同的是,race 表示“竞争;角逐”的意思,Promise.race 的可迭代对象的元素转换后的期约谁先 settled 谁就返回谁。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1041629.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

深入浅出Java的多线程编程——第一篇

目录 1. 认识线程&#xff08;Thread&#xff09; 1.1 概念 1.1.1 线程是什么 1.1.2 为啥需要线程 1.1.3 进程和线程的区别 1.1.4 Java的线程和操作系统线程的关系 1.2 第一个多线程程序 1.3 创建线程的方式&#xff08;5种&#xff09; 1.3.1 继承Thread类 1.3.2 实现…

电脑开机慢问题的简单处理

电脑用久了&#xff0c;开机时间要10-20分钟特别慢&#xff0c;一下介绍两种简单有效处理方式&#xff0c;这两种方式经测试不会影响原系统软件的使用&#xff1a; 方式一&#xff1a;禁用非必要启动项【效果不是很明显】 利用360里面的优化加速禁用启动项【禁用启动项还有其…

红色模板和黑色模板的区别

红色建筑模板和黑色建筑模板是常见的建筑支模材料&#xff0c;它们在颜色、材料、性能和适用范围等方面存在显著的区别。下面将详细介绍这两种建筑模板的区别。 首先&#xff0c;红色建筑模板通常由胶合板或其他木材制成&#xff0c;外观呈红色&#xff0c;而黑色建筑模板则采用…

MySQL数据库入门到精通6--进阶篇(锁)

5. 锁 5.1 概述 锁是计算机协调多个进程或线程并发访问某一资源的机制。在数据库中&#xff0c;除传统的计算资源&#xff08;CPU、RAM、I/O&#xff09;的争用以外&#xff0c;数据也是一种供许多用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据库必须解决…

沐风老师3DMAX刀剑轨迹拖尾插件SwordTrails使用方法详解

3DMAX刀剑轨迹拖尾插件SwordTrails使用教程 SwordTrail刀剑轨迹拖尾插件&#xff0c;是一款简单的运动轨迹特效工具。 【适用版本】 3dmax2011-2023&#xff08;不仅于此范围&#xff09; 【安装方法】 该插件无需安装&#xff0c;使用时直接拖动插件脚本文件到3dmax视口中打…

联机手写汉字识别系统技术要求与测试规程

声明 本文是学习GB-T 18790-2010 联机手写汉字识别系统技术要求与测试规程. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1 范围 本标准规定了联机手写汉字识别系统的技术要求和测试规程。 本标准适用于微型计算机、手持式信息处理设备等数字化设…

功能定义-后方碰撞预警

功能概述 后方碰撞预警(Rear Collision Warning)&#xff0c;简称RCW&#xff0c;其功能表现为实时监测车辆后方环境&#xff0c;并在可能受到后方碰撞危险时发出警告信息 报警区域 其中&#xff1a; L&#xff1a;表示后方盲区&#xff0c;受布置及传感器FOV影响 W&#xff1…

Java实现byte数组与Hex互转

十六进制字符的输出大写字符&#xff1a;0123456789ABCDEF 十六进制字符的输出小写字符&#xff1a;0123456789abcdef下面使用十六进制大写字符。 1、方式1 public class HexStringUtils {private static final char[] HEX_CHAR_TABLE {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B,…

00-MySQL数据库的使用-上

一 数据库基础知识 先谈发音 MySQL如何发音&#xff1f;在国内MySQL发音有很多种&#xff0c;Oracle官方文档说 他们念作 My sequal[si:kwəl]。 数据库基本概念 数据 数据&#xff08;Data&#xff09;是指对客观事物进行描述并可以鉴别的符号&#xff0c;这 些符号是可识别…

一篇文章全面解析Modbus协议中的消息帧

在 Modbus网络通信的两种传输模式中&#xff08; ASCII或RTU&#xff09;&#xff0c;传输设备以将Modbus消息转为有起点和终点的帧&#xff0c;这就允许接收的设备在消息起始处开始工作&#xff0c;读地址分配信息&#xff0c;判断哪一个设备被选中&#xff08;广播方式则传给…

MySQL 基础

本系列文章为【狂神说 Java 】视频的课堂笔记&#xff0c;若有需要可配套视频学习。 1. 简介 数据库&#xff08;DB&#xff0c;Database&#xff09;是安装在操作系统上的存储数据的软件。 关系型数据库&#xff08;RDB&#xff09;以行列形式存储数据。 非关系型数据库&am…

如何访问TDH中Inceptor 底层的元数据库TxSQL

如何访问TDH中Inceptor 底层的元数据库TxSQL 1 Inceptor概述 在大数据生态系统中&#xff0c;HIVE是离线数据仓库事实上的标准&#xff0c;绝大多数的大数据分析型系统或数据仓库系统&#xff0c;都是基于HIVE来构建的。 在星环的大数据平台TDH中&#xff0c;在功能上对应开…

PWN环境搭建

虚拟机Ubuntu安装 工具&#xff1a;Vmware 16 以及 Ubuntu 18或20 来源&#xff1a;清华大学开源软件镜像站 | Tsinghua Open Source Mirror 虚拟机安装流程 安装很简单&#xff0c;按照提示一步步来即可 处理器可以多给一些&#xff0c;我给了8个&#xff0c;内核数量不…

CCS介绍

CCS介绍 设置主体颜色 修改字体的颜色和大小 安装一些插件 CCS中的App中心 切换工作空间 导入工程

CarbonData详细解析

一、CarbonData简介 CarbonData是一种新型的Apache Hadoop本地文件格式&#xff0c;使用先进的列式存储、索引、压缩和编码技术&#xff0c;以提高计算效率&#xff0c;有助于加速超过PB数量级的数据查询&#xff0c;可用于更快的交互查询。同时&#xff0c;CarbonData也是一种…

AnyDesk多ID集中控制台V2.0

网盘下载 AnyDesk多ID集中控制台V2.0 软件介绍&#xff1a; 首先大家要知道AnyDesk软件是干嘛的&#xff1f;国外的远程协助工具&#xff0c;和TeamViewer同一个软件&#xff0c;TeamViewer确定需要登录&#xff0c;使用限制5分钟等等缺点&#xff0c;所以自己就用易语言开发An…

uni-app:实现页面效果1

效果 代码 <template><view><view class"add"><image :src"add_icon" mode""></image></view><view class"container_position"><view class"container_info"><view c…

69.渲染函数如何提高Vue应用程序的效率

通过使用虚拟 DOM&#xff0c;Vue 可以比直接操作真实 DOM 更高效地更新和渲染用户界面。渲染函数可用于在服务器上预渲染组件&#xff0c;从而提高应用程序的初始加载性能。渲染函数可让我们完全控制组件的结构和内容&#xff0c;从而构建自定义的复杂用户界面。 h() 函数&…

Java笔记:手写spring之aop

手写spring之aop 仓库地址: Raray-chuan/mini-spring 博文列表: 导读手写spring之ioc手写spring之aop手写spring之简单实现springboot 1.什么是AOP AOP(Aspect-oriented Programming), AOP翻译过来叫面向切面编程, 核心就是这个切面. 切面表示从业务逻辑中分离出来的横切…

【排序算法】冒泡排序、插入排序、归并排序、希尔排序、选择排序、堆排序、快速排序

目录 几大排序汇总 1.冒泡排序 性能: 思路和代码: 2.插入排序 性能: 思路和代码: 3.归并排序 性能: 思路和代码: 4.希尔排序 性能: 思路和代码: 5.选择排序 性能: 思路和代码: 6.堆排序 性能: 思路和代码: topK问题 7.快速排序 性能: 思路和代码: 几大排…