回调地狱和Promise
Posted So istes immer
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了回调地狱和Promise相关的知识,希望对你有一定的参考价值。
1.回调地狱callback-hell
由于fs.readFile是异步操作,所以你不能判断下面三个文件的执行顺序
var fs = require('fs')
fs.readFile('./data/a.txt','utf-8',function(err, data) {
if(err) {
throw err
}
console.log(data)
})
fs.readFile('./data/b.txt','utf-8',function(err, data) {
if(err) {
throw err
}
console.log(data)
})
fs.readFile('./data/c.txt','utf-8',function(err, data) {
if(err) {
throw err
}
console.log(data)
})
通过回调嵌套的方式可以保证执行顺序
所谓回调嵌套,就是一个异步请求的回调函数中含有另一个异步请求
var fs = require('fs')
fs.readFile('./data/a.txt','utf-8',function(err, data) {
if(err) {
throw err
}
console.log(data)
fs.readFile('./data/b.txt','utf-8',function(err, data) {
if(err) {
throw err
}
console.log(data)
fs.readFile('./data/c.txt','utf-8',function(err, data) {
if(err) {
throw err
}
console.log(data)
})
})
})
嵌套多了,就成了回调地狱(callback hell),缺点就是代码丑,而且不方便维护
2.Promise
为了解决上面这种回调地狱,ES6语法新增了一个API:Promise
Promise本身是一个容器,里面往往封装一个异步任务
promise有三种状态:Pending、Resolved、Rejected
对上面的例子用Promise改造一下
const fs = require('fs')
function pReadFile(filePath) {
return new Promise(function(resolve, reject) {
fs.readFile(filePath, 'utf-8', function(err, data) {
if(err) {
reject(err)
} else {
resolve(data)
}
})
})
}
pReadFile('./data/a.txt')
.then(function(data) {
console.log(data)
return pReadFile('./data/b.txt')
})
.then(function(data) {
console.log(data)
return pReadFile('./data/c.txt')
})
.then(function(data) {
console.log(data)
})
3.应用场景举例
有时候我们需要拿到多个接口的数据来渲染页面
安装json-server: npm install --g json-server
json-server可以直接把一个json文件托管成一个具备全RESTful风格的API,并支持跨域、jsonp、路由订制、数据快照保存等功能的 web 服务器。
执行命令:json-server --watch data.json(默认端口是3000,也可以自己指定端口)
通过启动json-server服务并侦听data.json,就可以将data.json文件托管成一个 web 服务,此时data.json就充当了数据库服务器的作用。
安装art-template: npm i art-template
demo.html(传统写法)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<form id="user_form">
</form>
<script src="node_modules/art-template/lib/template-web.js"></script>
<script type="text/template" id="tpl">
<div>
<label for="">用户名</label>
<input type="text" name="" value="{{ user.username }}">
</div>
<div>
<label for="">年龄</label>
<input type="text" name="" value="{{ user.age }}">
</div>
<div>
<label for="">职业</label>
<select>
{{ each jobs }}
{{ if user.job === $value.id }}
<option selected>{{ $value.name }}</option>
{{ else }}
<option>{{ $value.name }}</option>
{{ /if }}
{{ /each }}
</select>
</div>
</script>
<script>
get('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)
})
document.querySelector('#user_form').innerHTML = htmlStr
})
})
function get(url, callback) {
var oReq = new XMLHttpRequest()
oReq.onload = function() {
callback(oReq.responseText)
}
oReq.open("get", url, true)
oReq.send()
}
</script>
</body>
</html>
demo.html(使用jQuery,安装jquery:npm i jquery)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<form id="user_form">
</form>
<script src="node_modules/art-template/lib/template-web.js"></script>
<script src="node_modules/jquery/dist/jquery.js"></script>
<script type="text/template" id="tpl">
... // 原代码不变
</script>
<script>
var data = {}
$.get('http://127.0.0.1:3000/users/1')
.then(function(usersData) {
data.user = usersData
return $.get('http://127.0.0.1:3000/jobs')
})
.then(function(jobsData) {
data.jobs = jobsData
var htmlStr = template('tpl', data)
document.querySelector('#user_form').innerHTML = htmlStr
})
</script>
</body>
</html>
demo.html(使用Promise封装ajax方法)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<form id="user_form">
</form>
<script src="node_modules/art-template/lib/template-web.js"></script>
<script src="node_modules/jquery/dist/jquery.js"></script>
<script type="text/template" id="tpl">
... //原代码不变
</script>
<script>
var data = {}
get('http://127.0.0.1:3000/users/1')
.then(function(usersData) {
data.user = usersData
return get('http://127.0.0.1:3000/jobs')
})
.then(function(jobsData) {
data.jobs = jobsData
var htmlStr = template('tpl', data)
document.querySelector('#user_form').innerHTML = htmlStr
})
function get(url) {
return new Promise(function(resolve, reject) {
var oReq = new XMLHttpRequest()
oReq.onload = function() {
resolve(JSON.parse(oReq.responseText))
}
oReq.onerror = function(err) {
reject(err)
}
oReq.open("get", url, true)
oReq.send()
})
}
</script>
</body>
</html>
data.json
{
"users": [
{
"id": 1,
"username": "admin",
"age": 18,
"job": 3
},
{
"id": 2,
"username": "admin2",
"age": 17,
"job": 2
},
{
"id": 3,
"username": "admin3",
"age": 19,
"job": 3
}
],
"jobs": [
{
"id": 1,
"name": "学生"
},
{
"id": 2,
"name": "教师"
},
{
"id": 3,
"name": "医生"
},
{
"id": 4,
"name": "程序员"
}
]
}
4.操作数据库使用Promise
下面是mongoose操作数据库时使用promise的例子
const mongoose = require('mongoose')
var Schema = mongoose.Schema
mongoose.connect('mongodb://localhost/test')
var userSchema = new Schema({
username: {
type: String,
required: true
},
password: {
type: String,
required: true
},
email: {
type: String
}
})
var User = mongoose.model('User', userSchema)
// 注册用户
User.findOne({
username: 'zs'
}).then(function(user) {
if(user) {
console.log('用户已存在')
} else {
return new User({
username: 'zs',
password: '123456',
email: 'admin@admin.com'
})
}
}).catch()
以上是关于回调地狱和Promise的主要内容,如果未能解决你的问题,请参考以下文章
JavaScript 使用Async 和 Promise 完美解决回调地狱