回调地狱和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的主要内容,如果未能解决你的问题,请参考以下文章

promise解决回调地狱

回调地狱和Promise

JavaScript 使用Async 和 Promise 完美解决回调地狱

回调地狱以及用promise怎么解决回调地狱

javascript异步代码的回调地狱以及JQuery.deferred提供的promise解决方式

Promise是什么?怎么使用?回调地狱