网页javascript加载不出_写给初学者的JavaScript异步编程和背后思想

导读:对于接触JavaScript这门编程语言没有多久的本菜鸡而言,在相当长的一段时间内,我都完全无法理解这门语言中的异步编程,不明白什么叫异步编程以及为什么需要异步编程。为什么顺序执行程序就不行了呢?非要使用异步回调的方式来去做?经过一段时间的学习和探究,我算是初步了解了其中的道理和内涵。如果你像我一样也是一个JavaScript的小白,希望你看完我写的这篇文章之后,也可以解答你内心的很多困惑,并对JavaScript这门编程语言可以有一个更为深入的了解。

从单线程语言讲起

很长一段时间,我对JavaScript语言的困惑来自于我不清楚什么是**单线程编程语言**,而这个特性对于JavaScript走向异步编程的方式至关重要。在我们开始去讲单线程编程语言之前,有必须先去了解什么是进程,什么是线程。对这两个概念如果用纯文字讲解略显苍白,这里推荐一个B站Up主对线程和进程讲解的视频。我就是看完这个视频之后才明白进程和线程之间的关系的,相信你看完之后也可以理解。

有了之前的概念基础,下面就来谈谈什么是单线程编程语言。单线程顾名思义就是一个进程里面只会有一个线程。这一个线程有的时候也会被称之为主线程。如果一个进程里面只能有一个线程的话,那它就只能串行的执行程序,就是只能执行完A任务,然后再去执行B任务。反之,如果一个进程里面有多个线程的话,那么它看起来好像是可以并行的执行多个任务。对于单核CPU来说,系统会在多个线程之间快速不停地切换,就可以给你一种错觉好像是多个线程在并行的执行,但这只是一种错觉而已。真正的并行应该是多核才能实现。关于**串行、并行和并发**这几个概念,引用知乎某大佬一个形象的回答:

你吃饭吃到一半,电话来了,你一直到吃完了以后才去接,这就说明你不支持并发也不支持并行。
你吃饭吃到一半,电话来了,你停了下来接了电话,接完后继续吃饭,这说明你支持并发。
你吃饭吃到一半,电话来了,你一边打电话一边吃饭,这说明你支持并行。
并发的关键是你有处理多个任务的能力,不一定要同时。并行的关键是你有同时处理多个任务的能力。所以我认为它们最关键的点就是:是否是『同时』。

多线程就类似于并发这样的一个方式。只有多核才能做到真正的并行。

那么为什么JavaScript会采用这种单线程编程语言的方式呢?可以设想一下,如果JavaScript是一门支持多线程编程语言(比如Java),而多线程实际上是CPU在多个线程之间来回快速切换而已,当JavaScript运行在浏览器中,这时一个线程说要删除一个图片,另外一个线程说要添加一个图片,如果有上百个线程都在争抢CPU的运行时间的话,你的浏览器页面就会有很大的不确定性了,我们并不希望自己的页面加载、更新有很大不确定性。而如果一个进程只有一个线程的话,就不会有上述问题,任务一个接一个顺序执行就可以了。

单线程语言的问题

单线程的确可以不用考虑烦人的哪个任务先执行,哪个任务后执行的问题了,也不需要考虑多线程之间不同线程数据同步的问题。但是线程有他自身的问题。因为单线程是按顺序执行,如果一个任务执行的很慢的话,后面的任务全部都会被阻塞,执行不了。

bdf131d283a9cb7ee73676ca875391bf.png

想象你去超市买东西,如果前面有一个人结账很慢,那你也只能干等着,别的什么事也做不了。对于任务也是同样的道理,对于浏览器而言。我们通常需要各种各样的网络请求,去获取图片,视频等网络资源。如果一个网络请求很慢,比如某一个图片加载很慢,那后面的所有的数据都只能等着前面图片加载完之后才能再去访问网络加载。但我们实际用浏览器访问网页体验好像并不是这样。比如有的时候可能某一个图片加载很长时间也没有加载出来,但这并没有影响后面的图片或者视频等资源的加载。我们是如何做到这一点的呢?答案是使用异步编程(终于讲到异步编程了^ -- ^)

想不清楚是因为忘了考虑宿主环境!

我们首先引用一下lynnelv关于同步和异步的定义。

cbe18d756d0db5e8d57c52a8faef39a4.png

老实说,看完之后我其实是挺疑惑的!JavaScript是一个单线程语言,你比如说你有一个调用栈,就像在超市排队结账一样,按顺序执行程序,这很好理解。但怎么又出现了一个任务队列!单线程还能一边搞调用栈还能一边去做任务队列吗?!,举一个例子:

function updateAsync() {var i = 0;function updateLater() {document.getElementById('output').innerHTML = (i++);if (i < 1000) {setTimeout(updateLater, 0);}}updateLater();
}

比如上面代码展示的setTimeout是一个异步执行程序(书上就是这么说的),但是!单线程怎么可能会做到异步呢?假设你去排队结账,而且因为单线程,所以只能有一个结账通道,那么前面人结账再久,你也只能等着。你不能换一个通道去结账。但这个任务队列就是告诉你,我们还有一个结账通道。比如这个`setTimeout`是异步函数,他的延时计时,不会影响你的主线程执行。可是如果你只有一个线程,要么去执行程序,要么去计时,怎么可能一边计时,一边还执行程序呢?

后来我才明白,原来是这么回事!除了JavaScript本身之外,还有一个宿主环境。

JavaScript本身是不能自己单独执行的,要么在浏览器中,要么是在Node.js中,而上述两个就是宿主环境。JavaScript本身是单线程编程语言这没错,但JavaScript的宿主环境给他提供了额外的并发功能。(也就是上面说的消息队列)

c299481a9aadb9926ed5eb07711dce71.png
上图中,主线程运行的时候,产生堆(heap)和栈(stack),栈中的代码调用各种外部API,它们在"任务队列"中加入各种事件(click,load,done)。只要栈中的代码执行完毕,主线程就会去读取"任务队列",依次执行那些事件所对应的回调函数。

上图和对应的文字内容引用自[JavaScript 运行机制详解:再谈Event Loop](JavaScript 运行机制详解:再谈Event Loop),图中的WebAPIs就是JavaScript宿主环境所提供的线程。setTimeout会在该线程中执行,并不会影响JavaScript自身的主线程。`setTimeout`的延时时间结束之后,它就会进入到图中的回调消息队列中,当主线程空闲时,消息队列中的内容就会进入到主线程中去执行。

有了异步编程这样一个东西之后,JavaScript即使是单线程编程,也可以有并发的特性了。将网络请求和I/O操作等耗时的任务都交给异步编程来实现,主线程把控整体流程,这样主线程就不会被耗时任务拖累而发生阻塞。

Ajax

我们之前已经花了很大的篇幅来介绍异步编程和背后的思想。在这一部分我们谈谈对于浏览器而言,最重要的异步编程应用AJAX。

Ajax 即“Asynchronous Javascript And XML”(异步 JavaScript 和 XML),是指一种创建交互式、快速动态网页应用的网页开发技术,无需重新加载整个网页的情况下,能够更新部分网页的技术。
通过在后台与服务器进行少量数据交换,Ajax 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。

因为异步操作往往需要用到callback,如果不太清楚callback的话,可以看一下[这篇文章](方应杭:「每日一题」Callback(回调)是什么?),里面的关于callback的比喻很形象。因为异步操作不是同步任务,把异步请求或者操作发送出去之后就继续执行同步任务了,所以很适合用这种执行完之后“打电话”回调的方式执行任务。

知道callback的原理之后我们继续回到AJAX。下面这段代码是模拟AJAX请求的代码:

function ajax(url, callback) {// 1、创建XMLHttpRequest对象var xmlhttpif (window.XMLHttpRequest) {xmlhttp = new XMLHttpRequest()} else { // 兼容早期浏览器xmlhttp = new ActiveXObject('Microsoft.XMLHTTP')}// 2、发送请求xmlhttp.open('GET', url, true)xmlhttp.send()// 3、服务端响应xmlhttp.onreadystatechange = function () {if (xmlhttp.readyState === 4 && xmlhttp.status === 200) {var obj = JSON.parse(xmlhttp.responseText)// console.log(obj)callback(obj)}}
}

当服务器有响应之后,把响应数据,通过回调函数返回回来。注意之前文章中说的回调函数特点,是程序执行完把响应回调回来。在服务器响应这种异步的情况下,是很适合使用回调函数的。我们执行上述ajax的代码可以如下:

var url = 'https://getman.cn/mock/route/to/demo'
ajax(url, res => {console.log(res)
})

这个URL路径是一个在线测试网站,你可以[点击这里](MockServer 在线API接口模拟 - Getman)查看该网站详情。如上所示,通常情况下我们定义回调函数的时候,是写成箭头函数的形式,在该函数里面具体定义要执行什么样的回调动作。为了便于理解,我把箭头函数改成普通函数的形式,代码如下:

ajax(url, function(res) {console.log(res)
})

最后再强调一下,我们是把一个函数作为参数传递进去了。这个函数就是回调函数。

理解了AJAX和回调函数之后,我们再来看下面一种情况。在实际编程中,我们经常会遇到执行某些请求,然后有了反馈再出发a动作,a动作执行完,有了反馈再触发b动作......。举一个例子,比如客户端请求一段视频内容,这时候服务器不会将整段视频全部传过去,而是切分成很多小段,一段一段传给客户端。客户端得到一段视频之后确认无误,会再次发送请求,服务端就会再传过来下一段内容。而这些操作都是要异步完成,如果我们写成回调函数,大概可能会长这样:

ajax(url, res => {dosomethingajax(url, res => {dosomethingajax(url, res => {dosomething......})})
})

很可能会嵌套非常多的层数,从可读性来讲,非常的差,这种代码结构称之为**回调地狱**。对于这种回调地狱的写法问题,我们可以通过promise来解决。

Promise

ES6中新增一个引用类型称之为Promise,可以通过new操作符来实例化

我们来举一个简单例子:

let p = new Promise(() => {setTimeout(() => {console.log('wait one second')}, 1000)
})

但需要特别注意的,特别是对于初学者而言,我上面虽然写的setTimeout是异步执行,如果去执行代码的话,也的确会停留1s之后再去输出。但这并不表示new Promise里面的所有代码都会异步执行。我们下面举一个例子:

let p = new Promise(() => {console.log(1)
})
console.log(2)

如果你打印输出的话,会发现,先输出的1,后输出的2。显然,执行console.log(2)是同步任务,如果Promise是异步的话,应该先打印2后打印1。因此,需要特别注意的是,Promise里面的代码是同步任务,是立刻执行的。可能这时候你有点糊涂了,我了解同步代码同步执行,异步代码异步执行(比如setTimeout)。那Promise有什么用呢?又怎么实现异步操作?别急,关于这一点,我们后面就会谈到。

对于Promise而言,它最重要的特点在于其具有**状态管理**的功能。它一共有三种状态:

pending

resolved

rejected

Promise的一个特点就是它只能从pending状态到resolved状态,或者从pending状态到rejected状态。不能反过来。或者它也可能一直处于pending状态,当我们new一个Promise的时候,它就是pending状态。

那这个状态有什么用?举一个简单例子:

let p = new Promise((resolve, reject) => {console.log('进入Promise')resolve('成功')// 因为promise状态是不可逆的,所以reject实际是执行不了的reject('失败')
}).then(res => {console.log(res)
}, err => {console.log(err)
})

你可以执行一下上述代码,看看会输出什么。

我们在new Promise的时候传入两个参数,分别是resolve和reject(需要注意的是这两个参数都是函数)。调用resolve()会把状态切换成resolved,调用reject()会把状态切换成rejected(同时会抛出一个错误)。就像我们前面说的,状态是不可逆的。所以上述代码执行完resolve()之后,reject()是不会被执行的。

在上面的代码中我们还看到`.then`方法,`Promise.prototype.then()`是为Promise实例添加处理程序的主要方法。执行上述代码我们也可以看到,resolve()或者reject()所传递的参数会进入到then中进行对应的处理。

通过上面的例子,我们就可以大致勾勒出Promise的应用场景。比如客户端通过Promise发送一个异步请求给服务器。服务器如果响应成功,就在resolve所对应的then中去执行对应代码,如果响应失败,就进入到reject,在reject对应的then中去执行对应代码。

另外,关于请求失败的代码,除了用then的第二个参数捕获之外,还可以写成`catch`的形式:

let p = new Promise((resolve, reject) => {console.log('进入Promise')resolve('成功')reject('失败')
}).then(res => {console.log(res)
}).catch(err => {console.log(err)
})

以上,关于Promise最为基础的部分介绍的已经足够多了,下面我们尝试用Promise来调用一下AJAX请求。

let p = new Promise((resolve, reject) => {console.log('客户端向服务端发送请求')ajax(url, res => {console.log('第一次获得的响应结果是', res)resolve('成功')})
}).then(resolveRes => {console.log('请求结果:', resolveRes)return new Promise((resolve, reject) => {ajax(url, res => {console.log('第二次获得的响应结果是', res)resolve('成功')})    })
}).then(resolveRes => {console.log('请求结果:', resolveRes)return new Promise((resolve, reject) => {ajax(url, res => {console.log('第三次获得的响应结果是', res)resolve('成功')})    })
})

上述代码模拟的是比如客户端多次向服务器发送视频请求的功能代码。看起来一定程度上减轻了回调地狱这种糟糕写法,将嵌套结构变成了链式结构。但看起来好像非常糟糕。一方面是大量重复代码,非常冗余,另一方面,从理解层面也没有改善多少。关于第二点,后面我们会介绍一种新的语法结构:`async`,`await`。不过现在我们还是继续回到Promise中。

上面的代码,需要特别强调的一点是,每次我们new一个Promise,都会加一个`return`,为什么要写return呢?

比如在第二次请求的那个对应的Promise中不写return的话,意味着就不会有返回值。后面的then执行,相当于对战原有的(就是最开始的那个)Promise对象的then继续再执行then(返回的会是一个空的promise)。只有return 之后,才意味着对新new出来的Promise再执行then。因此在书写代码的时候一定要记得加return,否则如果发生多层Promise嵌套的时候很有可能出现逻辑错误。

下面我们把上面代码中重复部分进行抽离,精简一下代码可以写成这样:

function getPromise(url) {return new Promise((resolve, reject) => {ajax(url, res => {console.log('获得的响应结果是', res)})resolve('成功')})
}getPromise(url).then(resolveRes => {console.log('请求结果:', resolveRes)return getPromise(url)
}).then(resolveRes => {console.log('请求结果:', resolveRes)return getPromise(url)  
}).then(resolveRes => {console.log('请求结果:', resolveRes)return getPromise(url)    
})

经过抽离之后,代码看起来简洁很多。同样的,我们还是要再强调一下这个return问题。明明定义getPromise函数的时候已经写了return了,为什么后面再调用的时候还要再加return?因为函数定义部分可以看到,调用函数之后返回的相当于是一个new Promise,你可以对照之前写的比较复杂的代码,这个new Promise还需要再返回一下才行。

需要注意的一点是,在上面的例子中,then是平级的,也就是说,即使前一层请求失败,也不会影响后一层的then执行。如果你想对不同的请求失败做统一的处理,可以这样写代码:

getPromise(url).then(resolveRes => {console.log('请求结果:', resolveRes)return getPromise(url)
}).then(resolveRes => {console.log('请求结果:', resolveRes)return getPromise(url)  
}).then(resolveRes => {console.log('请求结果:', resolveRes)return getPromise(url)    
}).catch(err => {console.log(err)
})

这样的话,只要有一个请求失败,就会直接触发catch,别的then都不会被执行了。

此外,Promise还有一些静态方法,我这里就不演示了。具体可以看MDN的Promise文档。

async / await

除了使用Promise之外,我们还可以使用async/await来实现异步编程。

async关键字用于声明异步函数。这个关键字可以用在函数声明、函数表达式、箭头函数和方法上。此外需要注意的是,如同我们在介绍Promise中强调的一样,虽然async关键字可以让函数具有异步特征,但里面的同步代码还是会同步执行。只有遇到异步执行的函数时才会异步执行。

async的函数在执行后都会自动返回一个Promise对象,所以,我们也可以在async函数后面接then来做处理。不过我们还有一种更好的方式是使用await关键字。await可以获取后面Promise对象成功状态传递出来的参数。

我们下面举一个具体例子,还是调用之前在讲ajax部分定义的函数。

function getPromise(url) {return new Promise((resolve, reject) => {ajax(url, res => {console.log('获得的响应结果是', res)})resolve('成功')})
}// 改造后
function getPromise(url) {return new Promise((resolve, reject) => {ajax(url, res => {resolve(res)})})
}

我们把之前定义的getPromise函数略微改造一下。然后还是对之前的客户端向服务端多次请

async function getData(){const res1 = await getPromise(url)console.log(res1)const res2 = await getPromise(url)console.log(res2)const res3 = await getPromise(url)console.log(res3)
}
getData()

通过await等待,我们会在每次得到数据之后再发送下一次请求。这样异步编程看起来就像同步代码了,使代码变得更容易理解。

因为async返回的是Promise,所以,对于失败的请求,我们还是可以通过then的第二个参数或者catch来捕获:

getData().catch(err => {console.log(err)
})

参考资料

[1] [JavaScript异步编程](熊建刚:JavaScript异步编程)

[2] [说一说javascript的异步编程](说一说javascript的异步编程 - 陈术芳 - 博客园)

[3] [线程进程 |两个简单例子告诉你什么是进程和线程 | 进程线程原来如此简单](线程进程 |两个简单例子告诉你什么是进程和线程 | 进程线程原来如此简单_哔哩哔哩 (゜-゜)つロ 干杯~-bilibili)

[4] [并发与并行的区别是什么?](并发与并行的区别是什么?)

[5] [深入理解js事件循环机制(浏览器篇)](深入理解js事件循环机制(浏览器篇) - lynnelv's blog)

[6] [JavaScript 运行机制详解:再谈Event Loop](JavaScript 运行机制详解:再谈Event Loop)

[7] 《JavaScript高级程序设计(第4版)》

[8] 《深入浅出Nodejs》

[9] [ajax](ajax(Ajax 开发)_百度百科)

[10] [Callback(回调)是什么?](方应杭:「每日一题」Callback(回调)是什么?)

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

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

相关文章

静态网页托管_求职季,教你制作一份精美的在线网页简历,程序员必看!!

引言近期和学弟交流了一下找实习的相关的话题&#xff0c;谈到了简历这块。虽然近期没有找工作的打算&#xff0c;但还是会不定期的更新自己的简历。于是将自己的简历分享了一下&#xff0c;没想到得到了这样的评价&#xff0c;心里还是挺高兴的。简历的形式是一个在线的静态网…

为什么不可以使用哈曼顿距离_K-means真的不能使用曼哈顿距离吗?

问题说到k-means聚类算法&#xff0c;想必大家已经对它很熟悉了&#xff0c;它是基于距离计算的经典无监督算法&#xff0c;但是有一次在我接受面试时&#xff0c;面试官问了我一个问题&#xff1a;“k-means为什么不能使用曼哈顿距离计算&#xff0c;而使用欧式距离进行计算&a…

linux 查看进程_Linux怎么查看和监控每个进程的实时流量

请关注本头条号&#xff0c;每天坚持更新原创干货技术文章。如需学习视频&#xff0c;请在微信搜索公众号**“智传网优”**直接开始自助**视频学习**1. 前言NetHogs是一个开源的命令行工具(类似于Linux的top命令)&#xff0c;用来按进程或程序实时统计网络带宽使用率。来自NetH…

mysql修改密码1820_mysql5.7初始化密码报错ERROR1820(HY000):YoumustresetyourpasswordusingALTERUSERstateme...

1&#xff0c;mysql5.6是密码为空直接进入数据库的&#xff0c;但是mysql5.7就需要初始密码cat /var/log/mysqld.log | grep password或者&#xff1a;grep temporary password /var/log/mysqld.lo2&#xff0c;然后执行 mysql -uroot -p&#xff0c;输入上面的到的密码进入&am…

linux复制文件夹到另一个目录_Linux|一个命令行统计给定目录中有多少个子目录,学浪计划...

wc命令用于统计指定文件的字节数、字数、行数、并将统计结果显示出来。一般格式&#xff1a; wc [选项] [文件]选项&#xff1a;-c&#xff0c;--bytes 统计字节数-l&#xff0c;--lines 统计行数-w&#xff0c;--words 统计字数ls命令-l 以长格式显示文本的详细信息。-R 表示递…

mysql源码安装分析_MySQL源码分析(0):编译安装及调试(转)

编译安装为了实现MySQL的更高级别的性能调优&#xff0c;我们通常需要理解其内部实现机制&#xff0c;并对其进行优化调试。在下面的系列中&#xff0c;我们会分别介绍MySQL的部分内部实现机制。首先我们介绍如何从源代码部署一台MySQL服务器。1.下载MySQL Community Server源码…

前端做后台管理系统有前途吗_关于后台管理系统前端项目的思考

开发后台管理系统是大部分前端开发人员接触过的项目&#xff0c;如何更好的进行项目的搭建、组件的开发、数据结构的设计等等&#xff0c;这些都是需要考虑的问题。以下是我结合一些项目的经历和其他大佬的项目代码与技术分享&#xff0c;做出了对于后台管理系统中前端项目的思…

响应式编程优点 有效_什么是响应式编程?

响应式编程是一种通过异步和数据流来构建事物关系的编程模型。这里每个词都很重要&#xff0c;“事物的关系”是响应式编程的核心理念&#xff0c;“数据流”和“异步”是实现这个核心理念的关键。为了帮助大家理解这个概念&#xff0c;我们不妨以APP初始化业务为例来拆解一下这…

虚拟跳线软件干什么用的_视频教程:用 ESI 的虚拟跳线给你的声音添加效果

本视频讲述了如何在互联网上给你的声音添加效果。你可以使用任何想用的效果器&#xff0c;而且不仅可以输入你的声音&#xff0c;输入信号还可以是任何乐器音源&#xff0c;例如键盘或吉他&#xff0c;你甚至可以直播你的整个工程。请先看视频&#xff1a;视频中使用Bitwig给声…

极光推送java demo_极光推送- 3 分钟 Demo - 极光文档

3 分钟快速使用 JPush Android Demo本文目的在于&#xff0c;指导新接触极光推送的开发者&#xff0c;在短短几分钟时间内把极光推送跑起来&#xff1a;安装 Demo 客户端到手机在 Portal 上推送通知客户端收到推送并显示在状态栏创建极光推送开发者帐号Portal 上创建应用使用注…

java ldap添加用户名密码_java ldap用户密码md5加密

在这里不过多介绍ldap&#xff0c;因为这样的文章特别多&#xff0c;这里就简单直接的记录这一个问题。在springboot中通过引入spring-boot-starter-data-ldap&#xff0c;使用LdapTemplate真的挺方便&#xff0c;现在遇到一个问题&#xff0c;添加用户时&#xff0c;userPassw…

java dumpstack_Java获取执行进程的dump文件及获取Java stack

转发自https://blog.csdn.net/MCC_MCC_MCC/article/details/806231561.Windows/Linux环境下查看Java进程ID方法使用Java自带的工具VisualVM工具实现&#xff0c;在CMD或者是Linux终端下执行“jvisualvm”命令即可以进入VisualVM控制台&#xff0c;双击左侧的进程即可以查看到详…

mysql not in优化_实践中如何优化MySQL(收藏)

SQL语句的优化&#xff1a;1、尽量避免使用子查询3、用IN来替换OR4、LIKE前缀%号、双百分号、_下划线查询非索引列或*无法使用到索引&#xff0c;如果查询的是索引列则可以5、读取适当的记录LIMIT M,N&#xff0c;而不要读多余的记录6、避免数据类型不一致7、分组统计可以禁止排…

java 鼠标精灵_纯Java实现跨平台鼠标键盘模拟、找图找色,Java版按键精灵

由原本的Java使用JNI调用dll实现模拟辅助操作&#xff0c;升级到纯Java来实现&#xff0c;最新&#xff1a; https://github.com/xnx3/xnx31.[代码][Java]代码/*** 鼠标、键盘、延迟等基本操作*/public static void simple(){Robot robot new Robot();robot.delay(1000); //延…

o2oa二次开发比开发难吗_6年经验大牛,带你一起打开 Revit 二次开发的新世界大门...

​知乎视频​www.zhihu.com你好&#xff0c;这里是BIMBOX。一线的BIM工作者使用最多的软件是Revit&#xff0c;它功能强大&#xff0c;涵盖建筑、结构、MEP等专业&#xff0c;也正因为涵盖专业太多&#xff0c;它显得有点繁琐和笨拙&#xff0c;不仅约束了创造力&#xff0c;有…

python中的str方法和repr方法_Python中 的 __str__ 方法和 __repr__ 方法的区别有哪些

Python中 的 __str__ 方法和 __repr__ 方法的区别有哪些发布时间&#xff1a;2020-11-05 17:11:48来源&#xff1a;亿速云阅读&#xff1a;95作者&#xff1a;Leah本篇文章给大家分享的是有关Python中 的 __str__ 方法和 __repr__ 方法的区别有哪些&#xff0c;小编觉得挺实用的…

requestbody前端怎么传_学习前端开发前的基础知识了解「V1001」

一、什么是http协议?什么是协议&#xff0c;就是一群人协商好了&#xff0c;统一认知的规则。【例】你&#xff0c;我&#xff0c;他&#xff0c;还有她他他&#xff0c;大家一致认定结婚的时候都要给份子钱&#xff0c;这种大家认同的就是协议。http协议&#xff0c;就规定了…

postmapping注解参数说明_从零搭建后端框架:优雅的参数校验Validator

前两天项目群里发生了关于参数校验的问题讨论&#xff0c;很多开发团队没有对这些做硬性规范时&#xff0c;还是有很多童鞋本着“不多事”的原则&#xff0c;产品文档里没有特别说明就不写。对于2B的产品经理来说&#xff0c;因为一次新迭代&#xff0c;可能回涉及到N多的页面&…

ups计算软件_浅析UPS与蓄电池与逆变器的区别

早期的电信机房中&#xff0c;通常采用将220V交流电源经过整流&#xff0c;为48V电池组充电&#xff0c;由电池组直接给程控交换机供电。随着计算机网络和通信网络在电信机房的应用&#xff0c;需要为其提供高质量的220V的交流电源。由于有现有的48V电池组&#xff0c;所以通常…

django调用java_07.手把手教将深度学习利用Django将模型发布成服务供java调用

标题问题一.python发布成服务1.先建立一个深度学习模型并训练好2.建立一个预测方法去调用训练好的模型3.建立一个Django工程4.将python利用模型预测的代码放入Django项目中5.get请求加上参数6.github二.JAVA请求url&#xff0c;返回json数据问题python作为一个含科学计算与深度…