异步编程
回调函数
不成立的情况1:
function add(x, y) {
console.log(1)
setTimeout(function () {
console.log(2)
var ret = x + y
return ret
}, 1000)
console.log(3)
// 到这里执行就结束了,不会等到前面的定时器,所以直接返回了默认值undefined
}
console.log(add(10, 20))
// 1,3,undefined,2
不成立的情况2:
function add(x, y) {
var ret
console.log(1)
setTimeout(function () {
console.log(2)
ret = x + y
}, 1000)
console.log(3)
return ret
//到这里执行就结束了,不会等到前面的定时器,所以直接返回了默认值undefined
}
console.log(add(10, 20))
// 1,3,undefined,2
成立的情况(麻烦):
var ret
function add(x, y) {
console.log(1)
setTimeout(function () {
console.log(2)
ret = x + y
}, 1000)
}
add(10, 20)
setTimeout(function(){
console.log(ret)
},1000)
// 1,2,30要等一秒才会出来 add在定时函数之前
回调函数:
function add(x, y, callback) {
console.log(1)
// setTimeout异步操作
setTimeout(function () {
var ret = x + y
callback(ret)
}, 1000)
}
add(10, 20, function (ret) {
console.log(ret)
})
注意:凡是需要得到一个函数内部异步操作的结果setTimeout、readFile、writeFile、ajax
, 这种情况必须通过:回调函数
基于原生XMLHttpRequest封装get方法:
function get(url, callback) {
var oReq = new XMLHttpRequest()
// 当请求加载成功之后要调用指定的函数
oReq.onload = function () {
callback(oReq.responseText)
}
oReq.open("get", url, true)
oReq.send()
}
get('data.json', function (data) {
console.log(data)
})
Promise?
参考文档:http://es6.ruanyifeng.com/#docs/promise
promise是什么:是一个对象用来传递异步操作的信息,它代表了某个未来才会知道结果的事件(通常是一个异步操作),并且这个事件提供统一的api,可供进一步的处理。
promise的作用:Promise的出现主要是解决地狱回调的问题,比如结果需要请求很多个接口,这些接口的参数需要另外的接口返回的数据作为依赖,这样就需要我们一层嵌套一层,但是有了Promise 我们就无需嵌套。
promise的本质是什么:分离异步数据获取业务
callback hell回调函数
无法保证顺序:
// 无法保证顺序
fs.readFile('./data/a.txt','utf8',function(err,data){
if(err) throw err // 抛出异常:1.阻止程序的执行 2.把错误消息打印到控制台
console.log(data)
})
fs.readFile('./data/b.txt','utf8',function(err,data){
if(err) throw err
console.log(data)
})
fs.readFile('./data/c.txt','utf8',function(err,data){
if(err) throw err
console.log(data)
})
通过回环嵌套保证顺序:
fs.readFile('./data/a.txt', 'utf8', function (err, data) {
if(err) throw err
console.log(data)
fs.readFile('./data/b.txt', 'utf8', function (err, data) {
if(err) throw err
console.log(data)
fs.readFile('./data/c.txt', 'utf8', function (err, data) {
if(err) throw err
console.log(data)
})
})
})
为了解决以上编码方式带来的问题(回调地狱嵌套),所以在EcmaScript 6 中新增了一个API:Promise
Promise的英文就是承诺、保证的意思
Promise基本语法
var p1 = new Promise(function(resolve,reject){
fs.readFile('./data/a.txt','utf8',function(err,data){
if(err){
// 失败了,承诺容器中的任务失败了
// 把容器的Pending状态变为rejected
// 调用reject就相当于调用了then方法的第二个参数函数
reject(err)
}else {
// 承诺容器中的任务成功了
// 把容器的Pending状态变为resolved
// 调用的resolve方法实际上就是then方法传递的那个function
resolve(data)
}
})
})
// p1就是那个承诺
// 当p1成功了,然后(then)做指定的操作
// then方法接收的function就是容器中的resolve函数
p1.then(function(data){
console.log(data)
},function(err){
console.log('读取文件失败了',err)
})
promise-api嵌套
var p1 = new Promise(function(resolve,reject){
fs.readFile('./data/a.txt','utf8',function(err,data){
if(err){
reject(err)
}else {
resolve(data)
}
})
})
var p2 = new Promise(function(resolve,reject){
fs.readFile('./data/b.txt','utf8',function(err,data){
if(err){
reject(err)
}else {
resolve(data)
}
})
})
var p3 = new Promise(function(resolve,reject){
fs.readFile('./data/c.txt','utf8',function(err,data){
if(err){
reject(err)
}else {
resolve(data)
}
})
})
p1.then(function(data){
console.log(data)
// 当 return 一个 Promise 对象的时候,后续的 then 中的 方法的第一个参数会作为 p2 的 resolve
return p2
},function(err){
console.log('读取文件失败了',err)
})
.then(function(data){
console.log(data)
return p3
})
.then(function(data){
console.log(data)
console.log('end')
})
封装Promise版本的readFile
var fs = require('fs')
function pReadFile(filePath){
return new Promise(function(resolve,reject){
fs.readFile(filePath,'utf8',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)
})
Promise使用场景:
json-server --version
json-server --watch data.json
通过http启动页面:
hs -c-l -o
promise用户数据
<script type="text/template" id="tpl">
<div>
<label for="">用户名</label>
<input type="text" value="{{ user.username }}">
</div>
<div>
<label for="">年龄</label>
<input type="text" value="{{ user.age }}">
</div>
<div>
<label for="">职业</label>
<select name="" id="">
{{ each jobs }} {{ if user.job === $value.id }}
<option value="{{ $value.id }}" selected>{{ $value.name }}</option>
{{ else }}
<option value="{{ $value.id }}">{{ $value.name }}</option>
{{ /if }} {{ /each }}
</select>
</div>
</script>
<script src="node_modules/art-template/lib/template-web.js"></script>
// 在用jquery时才加
<script src="node_modules/jquery/dist/jquery.js"></script>
callback 方式的 API
function get(url, callback) {
var oReq = new XMLHttpRequest()
// 当请求加载成功之后要调用指定的函数
oReq.onload = function () {
callback(oReq.responseText)
}
oReq.open("get", url, true)
oReq.send()
}
按照上面的步骤没有成功:
// 用户表
// 其中一个接口获取用户数据
// 职业:2
// 职业信息表
// 其中一个接口获取所有的职业信息
get('http://127.0.0.1:3000/users/4',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
var data = {}
$.get('http://127.0.0.1:3000/users/4')
.then(function(user){
data.user = user
return $.get('http://127.0.0.1:3000/jobs')
})
.then(function(jobs){
data.jobs = jobs
var htmlStr = template('tpl',data)
document.querySelector('#user_form').innerHTML = htmlStr
})
封装promise的ajax方法
var data = {}
pGet('http://127.0.0.1:3000/users/4')
.then(function(user){
data.user = user
return $.get('http://127.0.0.1:3000/jobs')
})
.then(function(jobs){
data.jobs = jobs
var htmlStr = template('tpl',data)
document.querySelector('#user_form').innerHTML = htmlStr
})
function pGet(url,callback) {
return new Promise(function(resolve,reject){
var oReq = new XMLHttpRequest()
oReq.onload = function () {
callback && callback(JSON.parse(oReq.responseText))
resolve(JSON.parse(oReq.responseText))
}
oReq.onerror = function(err){
reject(err)
}
oReq.open("get", url, true)
oReq.send()
})
}
两种方法
// pGet('http://127.0.0.1:3000/users/4', function (data) {
// console.log(data)
// })
pGet('http://127.0.0.1:3000/users/4')
.then(function (data) {
console.log(data)
})
mongoose所有的API都支持promise
User.findOne({ username: '11qwe' })
.then(function(user){
if (user){
// 用户已存在,不能注册
console.log('用户已存在')
} else {
// 用户不存在,可以注册
return new User({
username: '11qwe',
password: '123456',
email: 'ewyucfghfdkjs'
}).save()
}
})
.then(function(ret){})
上述嵌套:
User.findOne({ username: 'aaa' }, function (user) {
if (user) {
console.log('已存在')
} else {
new User({
username: 'aaa',
password: '123',
email: 'dsadas'
}).save(function () { })
}
})