同步异步的理解问题1 为什么在开发过程中更推荐异步编程呢(如 async/await)? 下面描述一下异步编程的好处:

同步异步的理解问题1 为什么在开发过程中更推荐异步编程呢(如 async/await)? 下面描述一下异步编程的好处:

晚上睡不着觉,浅浅的看了几篇文章,梳理一下自己对同步异步问题的理解

问题1 为什么在开发过程中更推荐异步编程呢(如 async/await)?

下面描述一下异步编程的好处:

非阻塞性,使用async/await使代码在等待网络请求的响应过程中不会阻塞主进程,这里要解释一下,因为js是单线程的,意味着它只能同时执行一个任务,如果使用同步方式调用接口,代码会在请求完成之前完全停止执行,可能会导致页面”卡死“,用户可能会觉得应用无响应,体验感会不好,而使用异步编程可以让程序在等待接口响应导时继续执行其他操作,比如更新页面、点击按钮等其他操作。

错误处理

异步:在async/await函数中,可以使用try/catch语句来捕获异常中的错误.

同步:在同步代码中,错误处理可能会更复杂,尤其是在涉及多个异步操作时.

异步编程中async/await可以使代码更接近于同步代码结构,易于理解和维护,可以像写同步代码一样按照逻辑顺序书写异步代码,同时也避免了回调地狱的问题

**注:他俩最大的区别:异步请求允许多个请求同时进行,而不需要等待每个请求完成后再开始下一个,这种并发处理可以提高性能,尤其在需要同时在多个接口获取数据时。

问题2 异步编程和同步编程的使用场景分别是什么?

同步编程:小文件的读写、程序启动时的初始化操作、以及最常见的例子吧,绿-黄-红 灯的执行顺序

异步编程: 大文件的读写、网络请求、并发任务、定时任务

问题3 await可以单独使用吗?

答案是:当然不可以了,await必须和async配套使用,如果一个函数使用了async,他会返回一个promise,而await的作用就是暂停当前的async函数的执行,直到promise完成,如果单独使用await一定会报错,因为js引擎无法提供必要的异步上下文,导致语法错误,下面贴点代码举个例子:

const deleteTemplate = async (id: string) => {

console.log("1111111");

const result = await removeTemplate(id)

console.log("2222222");

if (result?.success) {

message.success({

content: "模板删除成功",

class: MESSAGE_CLS.SUCCESS,

});

await refreshList();

} else if (result?.message == "模板已被使用,不能删除") {

message.warn({

content: "模板已被使用,不能删除",

class: MESSAGE_CLS.WARN,

});

await refreshList();

} else {

message.warn({

content: result.message,

class: MESSAGE_CLS.WARN,

});

}

console.log("333333");

}

};

我之前一直有个疑问,都有await了,还要等待接口响应成功才执行后续的操作,那为啥不直接用同步编程呢,现在看到这些解释,嗯,可以说的通了哈哈,那考一下,这段代码中的执行顺序是什么?

1111111

2222222

333333

问题4 什么是Promise?

Promise是js中用于处理异步操作的一种机制,它代表一个可能在未来某个时间点完成的操作的结果。Promise可以处于三种状态之一:

Pending(待定):初始状态,既不是成功,也不是失败。

Fulfilled(已完成):操作成功完成,Promise也会变为成功状态。

Rejected(已拒绝):操作失败,Promise变为失败状态

Promise的基本用法

创建 Promise

可以使用Promise构造函数来创建一个新的Promise。构造函数接受一个执行器函数,该函数有两个参数:resolve和reject.

const myPromise = new Promise((resolve, reject) => {

// 异步操作

const success = true;

if (success) {

resolve("操作成功");

} else {

reject("操作失败");

}

});

使用Promise

// 第一种通过async/await(推荐!!!)

const handlePromise = async () => {

try {

// 等待 Promise 解析

const result = await myPromise;

// 处理成功的情况

console.log("成功:", result);

} catch (e) {

// 处理失败的情况

console.error("失败:", error)

}

}

handlePromise(); // 调用 async 函数

// 第二种 使用 then 和 catch 方法来处理 Promise 的结果

const handlePromise = () => {

myPromise.then((result) => {

console.log(result);

}).catch((err) => {

console.log(err);

})

}

handlePromise(); // 调用 函数

Promise.all的用法

Promise.all:接受一个Promise数组,只有当所有的Promise都成功时,才会返回一个成功的Promise。如果有任何一个Promise失败,则会返回失败的Promise

function fetchData(url) {

return new Promise((resolve) => {

setTimeout(() => {

resolve(`Data from ${url}`);

}, 1000);

});

}

const urls = ['url1', 'url2', 'url3'];

Promise.all(urls.map(fetchData)).then((results) => {

console.log(results); // ['Data from url1', 'Data from url2', 'Data from url3']

}).catch((error) => {

console.error('Error fetching data:', error);

});

回调地狱的问题

回调地狱(Callback Hell)是指在js 中使用回调函数处理异步操作时,代码层级嵌套过深,导致代码可读性差、维护困难的现象,通常发生在需要进行多个异步操作时,每个操作的结果都依赖于前一个操作的完成。

简单举个回调地狱的示例,假设我们需要依次执行三个异步操作,每个操作的结果都依赖于前一个操作

function asyncOperation1(callback) {

setTimeout(() => {

console.log("操作1完成");

callback("结果1");

}, 1000);

}

function asyncOperation2(resultFromOp1, callback) {

setTimeout(() => {

console.log("操作2完成,接收到:", resultFromOp1);

callback("结果2");

}, 1000);

}

function asyncOperation3(resultFromOp2, callback) {

setTimeout(() => {

console.log("操作3完成,接收到:", resultFromOp2);

callback("结果3");

}, 1000);

}

// 使用回调函数进行嵌套

asyncOperation1(result1 => {

asyncOperation2(result1, result2 => {

asyncOperation3(result2, result3 => {

console.log("所有操作完成,最终结果:", result3);

});

});

});

在说一下解决方案

先说最推荐的,使用 async/await,使异步代码看起来更像同步代码,进一步提高了可读性

async function executeOperations() {

try {

const result1 = await asyncOperation1();

const result2 = await asyncOperation2(result1);

const result3 = await asyncOperation3(result2);

console.log("所有操作完成,最终结果:", result3);

} catch (error) {

console.error("发生错误:", error);

}

}

executeOperations();

使用 Promise:Promise 可以将异步操作的结果封装起来,避免深层嵌套。

function asyncOperation1() {

return new Promise(resolve => {

setTimeout(() => {

console.log("操作1完成");

resolve("结果1");

}, 1000);

});

}

function asyncOperation2(resultFromOp1) {

return new Promise(resolve => {

setTimeout(() => {

console.log("操作2完成,接收到:", resultFromOp1);

resolve("结果2");

}, 1000);

});

}

function asyncOperation3(resultFromOp2) {

return new Promise(resolve => {

setTimeout(() => {

console.log("操作3完成,接收到:", resultFromOp2);

resolve("结果3");

}, 1000);

});

}

// 使用 Promise 链式调用

asyncOperation1()

.then(result1 => asyncOperation2(result1))

.then(result2 => asyncOperation3(result2))

.then(result3 => {

console.log("所有操作完成,最终结果:", result3);

})

.catch(error => {

console.error("发生错误:", error);

});

讲个在开发过程中遇到的问题

根据模板生成正式报告:见名知意,意识就是根据文件模板生成一份正式报告,也就是说要先把模板下载到本地,然后在本地进行占位符替换,生成新文件,再把新文件上传。看一段代码:

const generateReportDraft = async (

templateInfo: {

id: string;

name: string;

templateType: number;

type: string;

// 新

fileId: string;

path: string;

},

replaceForTempTemplate: ReplaceData["replaceForTempTemplate"],

projectId: string,

projectSubitemId: string, // 子项目的 id

sysCompanyCode: string | number,

dataDiskBasePath: string,

parentFolder: {

fileId: string;

relativePath: string;

absolutePath: string;

} // 初稿(临时模板)保存地址

): Promise<{

success: boolean;

localDir: string;

message?: string;

reportRecord?: Partial;

editorConfig?: any;

docService?: string;

}> => {

// debugger;

let localDir = "";

if (

!templateInfo.id ||

!projectId ||

!replaceForTempTemplate ||

!projectSubitemId ||

!parentFolder

) {

failRes.message = "缺少参数";

return failRes;

}

const {

id: templateId,

name: templateName,

type: templateFileExt,

path: templatePath,

} = templateInfo;

const templateRemotePath = dataDiskBasePath + templatePath;

const remotePath = parentFolder.absolutePath + "/" + templateName;

// 下载临时模版

const downloadRes = await downloadTemplateFileByApi(

{

name: templateName,

path: templateRemotePath,

templateId: templateId,

ext: templateFileExt

},

TransferChannel.DT

);

const templateLocalPath = downloadRes.result?.localPath || "";

localDir = downloadRes.result?.localDir || "";

failRes.localDir = localDir;

if (!downloadRes?.success || !templateLocalPath || !localDir) {

failRes.message = downloadRes.message || "下载模板失败";

return failRes;

}

// 循环最多尝试10次,检查模板文件是否存在 如果文件不存在,等待100毫秒后再次检查

// 因为接口下载文件 需要时间,会有文件不存在的情况

for (let i = 0; i < 10; i++) {

const {owner} = await checkExist("local", templateLocalPath);

if (owner && owner !== void 0) {

break;

}

await new Promise((resolve) => setTimeout(resolve, 100)); // 等待1秒

}

// 在本地下载的模版进行占位符替换

下面代码省略

};

其中的关键就在:

// 循环最多尝试10次,检查模板文件是否存在 如果文件不存在,等待100毫秒后再次检查

// 因为接口下载文件 需要时间,会有文件不存在的情况

for (let i = 0; i < 10; i++) {

const {owner} = await checkExist("local", templateLocalPath);

if (owner && owner !== void 0) {

break;

}

await new Promise((resolve) => setTimeout(resolve, 100)); // 等待1秒

}

因为是项目改造,之前下载文件用的是sftp直连下载,下载速度很快,并没有发现在本地进行替换的时候文件不存在的问题,但是这次改成接口下载之后,下载速度慢相对慢一些,就发现了问题所在,会有文件不存在的情况,当时也挺搞笑的,怎么也想不到因为什么,然后打断点因为会有时间差,在这个时间差内文件已经下载完了,所以打断点的时候一切正常,还看不出什么,后来请教前辈,前辈提点了一下,恍然大悟哈哈

好啦,就写到这里吧,写文章真的好费时间,只是个人理解,仅供参考哦

✧ 相关推荐 ✧

服务器为什么会回档如何处理
365bet体育官网网址

服务器为什么会回档如何处理

📅 06-27 👁️ 5301
方言俗语中的糙话“不作死就不会死”,实在是至理“名言”
主流的Web服务器有哪些
365bet体育官网网址

主流的Web服务器有哪些

📅 08-31 👁️ 1763