异步编程
得到函数内部异步操作的结果
回调函数:通过一个函数,获取函数内部的操作。(根据输入得到输出结果)
- 在该情况下无法获得函数内异步操作的结果
1 | function get(a, b) { |
结果:返回undefined
- 若要获得该数据则只能通过回调函数
1 | function get(a, b, c) { |
结果
注意:
凡是需要得到一个函数内部异步操作的结果(setTimeout,readFile,writeFile,ajax,readdir)
这种情况必须通过 回调函数 (异步API都会伴随着一个回调函数)
回调地狱
啥是回调地狱
就是这幅图(图源网络)
为什么会有回调地狱
回调地狱的原因是,当人们试图以一种从上到下的视觉方式执行JavaScript的方式编写JavaScript时。期望第1行发生的任何事情都会在第2行的代码开始运行之前完成,但是,在JavaScript上,有时候这并没办法进行,比如,在你通过异步读取文件时,也就是用fs模块读取多个文件时
1 | var fs = require('fs') |
执行多次后,你会发现,有那么几次,也有可能好几次,看人品吧反正是,它是没有规则的读出来的(往往可能你的文件越大,读出来的时间会更久),也就是说,它并不会按照代码书写顺序去执行,这便是异步编程(如果试了没有,那就一直试,反正总会有的)。异步API导致了代码并不是按顺序执行的(可以读读这篇文章 https://www.jianshu.com/p/39adf6ab8ad1 ——然后嘞,就会有上面那种解决方法,但是你会发现,代码非常的丑(别人是这样说的,反正我不是太这么认为,甚至觉得有点好看),还有非常难维护(这点认同)。所以就出现了几种解决方法 —Promise
Promise
简介
- Promise:承诺,保证
- Promise本身不是异步的,但往往都是内部封装一个异步任务
丢出一张图形容Promise函数,相当于一个容器(下图源于所学教程,pending(悬而未决的))
代码如下,较易维护
1 | var fs = require('fs') |
then函数(ES6)说明:
封装Promise中的readFile
方法
1 | var fs = require('fs') |
Promise应用场景
解决客户端回调嵌套问题
当出现类似于表关联的数据时,这时候就会遇到嵌套问题,当嵌套的数据只有一两个个还好,如果出现三四个甚至五六个,这时候就会出现回调地狱的问题,这里使用promise解决
所需知识:
- npm模块:json-server、http-server
- 客户端模板引擎art-template
- Ajax
- jquery
步骤
安装json-server和http-server以及其他必要模块
1
2
3
4npm i -g http-server
npm i -g json-server
npm i jquery --save
npm i template --save建立html页面
设计表单,人员信息与工作职业相关联,搭配模板字符串使用
1
<form action="" id="user_form"></form>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20<script type="text/template" id="tpl">
{{ user.username }}">
{{ user.age }}">
{{ each jobs }} {{ if user.job === $value.id }}
{{ $value.id }}" selected>{{ $value.name }}</option>
{{ else }}
{{ $value.id }}">{{ $value.name }}</option>
{{ /if }} {{ /each }}
</script>引用相关模板字符串以及JQuery模块
1
2<script src="node_modules/art-template/lib/template-web.js"></script>
<script src="node_modules/jquery/dist/jquery.js"></script>书写Ajax向服务器发起请求,并封装便于使用
1
2
3
4
5
6
7
8
9function get(url, callback) {
var oReq = new XMLHttpRequest()
oReq.onload = function () {
oReq.responseText
callback(oReq.responseText)
}
oReq.open("get", url, true)
oReq.send()
}开启json-server服务,使用data.json文件(6、7步使用cmd)
1
json-server data.json
将当前文件所处文件夹开放为服务器
1
http-server
- 若要禁用缓存,则使用以下命令
1
http-server -c-1
🔺请求代码
若采用回调地狱类型
1
2
3
4
5
6
7
8
9
10get("http://127.0.0.1:3000/users/1",function(userData){
get("http://127.0.0.1:3000/jobs",function(jobsData){
var htmlStr = template("tpl", {
user: JSON.parse(userData),
jobs: JSON.parse(jobsData)
})
console.log(htmlStr)
document.querySelector("#user_form").innerHTML = htmlStr
})
})使用Jquery版的Promise
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16//Jquery的Ajax自带promise
var data = {}
$.get("http://127.0.0.1:3000/users/2")
.then(function(user){
data.user = user
return $.get("http://127.0.0.1:3000/jobs")
})
.then(function(jobs){
data.jobs = jobs
// console.log(data)
var str = template("tpl",{
user: data.user,
jobs: data.jobs
})
document.querySelector('#user_form').innerHTML = str
})封装Promise版本的AJAX方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16function Rget(url,callback){
return new Promise(function(resolve,reject){
var xhr = new XMLHttpRequest()
// 当请求加载成功之后要调用指定的函数
xhr.onload = function () {
// 我现在需要得到这里的 xhr.responseText
resolve(JSON.parse(xhr.responseText))
callback && callback(JSON.parse(xhr.responseText))
}
xhr.onerror = function (err){
reject(err)
}
xhr.open("get", url, true)
xhr.send()
})
}使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14var data = {}
Rget("http://127.0.0.1:3000/users/2")
.then(function(user){
data.user = user
return Rget("http://127.0.0.1:3000/jobs")
})
.then(function(jobs){
data.jobs = jobs
var str = template("tpl", {
user: data.user,
jobs: data.jobs
})
document.querySelector("#user_form").innerHTML = str
})Promise操作数据库
- mongoose中所有的API都支持promise
🔺根据查询是否已存在该记录从而决定是否创建新记录
1 | Cat.findOne({ name: "好啊" }) |
注意:
- 每次改完js或html文件后在浏览器需刷新多次
- 每次改完json文件后需要重新启动json-server服务
catch异常处理
在全部then之后添加.catch(err => {})
即可对任何一个then处理过程抛出的异常进行捕获并中止代码继续执行
例如:读取文件并进行后续相关操作,若处理过程发生一个错误则传递给catch,则catch前面,出错误的then后面所有的then就不再执行
这里要注意区分,如果是在then
中自行处理err
,则代码还是会继续往下执行,这是和catch
不同的点
还有如果在catch后面继续then,则还是会继续执行下去
1 | readFile('a.txt', 'utf8') |