NodeJS七天课程学习笔记_第6天
课程内容概要:
1. Node 中如何操作Mysql数据库 (包括如何使用连接池pool)
2. 将上一个使用MongoDB的CRUB项目改写成使用Mysql
3. 针对回调地狱callback hell 而生的 Promise语法
4. 封装promise版本的自定义ajax的get方法
5. 演示一下promise的使用场景
6. 将前面的node_36_index.js这个mongoose的CRUD案例中的写法改成promise+then的写法
在npmjs.com中搜索mysql,进入使用介绍页面:https://www.npmjs.com/package/mysql
安装mysql:
npm install mysql –save
然后启动本机的Mysql,看一下db2库下面有什么表
根据文档写出node操作mysql的最简单的demo
node_37.js代码如下:
function NSLog(loli,needLogo=true) {console.log(loli);if(needLogo){console.log('\nCopyright © 2018 Powered by beyond')};}
// 1.导入框架
var mysql = require('mysql')
// 2.创建连接
var connection = mysql.createConnection({
host: 'localhost',
user: 'root',
password: '520',
database: 'db2'
})
// 3.连接数据库
connection.connect();
// 4.查询数据库
var sql = 'select 6+7 as girlAge'
connection.query(sql,function (error,results,fields) {
if(error){
return NSLog(error)
}
NSLog('girl is : ' + results[0].girlAge + '岁')
})
// 5.关闭数据库
connection.end()
效果如下:
下面尝试着把user表里的第一条记录查出来:
node_37_query1.js代码如下:
function NSLog(loli,needLogo=true) {console.log(loli);if(needLogo){console.log('\nCopyright © 2018 Powered by beyond')};}
// 1.导入框架
var mysql = require('mysql')
// 2.创建连接
var connection = mysql.createConnection({
host: 'localhost',
user: 'root',
password: '520',
database: 'db2'
})
// 3.连接数据库
connection.connect();
// 4.查询数据库
var sql = 'select name from user'
connection.query(sql,function (error,results,fields) {
if(error){
return NSLog(error)
}
NSLog('girl is : ' + results[0].name)
})
// 5.关闭数据库
connection.end()
效果如下:
通过软件查看本机数据库中的表和记录,这里使用Navicat Premium
打开Navicat Premium12.0.20之后,新建一个连接,如图所示:
然后就可以,连接本机mysql数据库,查看db2数据库里的user表了:
手动新建一个girls的表: (输入完字段名和类型后,Ctrl + S保存,输入表名)
补充一下: 为了安全起见, mysql中没有直接修改 数据库的名字的命令
现在通过node_37_add.js我们来插入一条记录到数据库,代码如下:
function NSLog(loli,needLogo=true) {console.log(loli);if(needLogo){console.log('\nCopyright © 2018 Powered by beyond')};}
// 1.导入框架
var mysql = require('mysql')
// 2.创建连接
var connection = mysql.createConnection({
host: 'localhost',
user: 'root',
password: '520',
database: 'db2'
})
// 3.连接数据库
connection.connect();
// 4.查询数据库
// 如果不指定字段名,则每一个都要填写值(主键可以用null代替)
var sql = 'insert into girls values(null,"面码",15,"未闻花名","2010-06-07")'
connection.query(sql,function (error,results,fields) {
if(error){
return NSLog(error)
}
NSLog('插入成功')
})
// 5.关闭数据库
connection.end()
效果如下:
多插入几个,再查询一下:
接下来根着文档写根据_id查询 node_37_findOneById.js代码如下:
(因为后面用Mysql重写CRUD项目中,根据_id进行查询出来后,进入修改页面)
function NSLog(loli,needLogo=true) {console.log(loli);if(needLogo){console.log('\nCopyright © 2018 Powered by beyond')};}
// 1.导入框架
var mysql = require('mysql')
// 2.创建连接
var connection = mysql.createConnection({
host: 'localhost',
user: 'root',
password: '520',
database: 'db2'
})
// 3.连接数据库
connection.connect();
// 4.查询数据库
var sql = 'select girlName,girlAge,girlDescription,pubTime from girls where _id = ?'
var queryObj = {
'sql': sql,
timeout: 6000,
values: ['2']
}
connection.query(queryObj,function (error,results,fields) {
if(error){
return NSLog(error)
}
var girlObj = results[0]
NSLog('根据_id = 2 查询成功: \n' + girlObj.girlName + ',' + girlObj.girlAge + ',' + girlObj.girlDescription + ',' + girlObj.pubTime)
})
// 5.关闭数据库
connection.end()
效果如下:
根据_id,删除一条记录node_37_deleteOneById.js代码如下:
function NSLog(loli,needLogo=true) {console.log(loli);if(needLogo){console.log('\nCopyright © 2018 Powered by beyond')};}
// 1.导入框架
var mysql = require('mysql')
// 2.创建连接
var connection = mysql.createConnection({
host: 'localhost',
user: 'root',
password: '520',
database: 'db2'
})
// 3.连接数据库
connection.connect();
// 4.查询数据库
var sql = 'delete from girls where _id = ?'
var queryObj = {
'sql': sql,
timeout: 6000,
values: ['5']
}
connection.query(queryObj,function (error,results,fields) {
if(error){
return NSLog(error)
}
NSLog('删除成功')
})
// 5.关闭数据库
connection.end()
效果如下:
查询所有的记录node_37_findAll.js代码如下:
function NSLog(loli,needLogo=true) {console.log(loli);if(needLogo){console.log('\nCopyright © 2018 Powered by beyond')};}
// 1.导入框架
var mysql = require('mysql')
// 2.创建连接
var connection = mysql.createConnection({
host: 'localhost',
user: 'root',
password: '520',
database: 'db2'
})
// 3.连接数据库
connection.connect();
// 4.查询数据库
var sql = 'select _id,girlName,girlAge,girlDescription,pubTime from girls'
connection.query(sql,function (error,results,fields) {
if(error){
return NSLog(error)
}
// 遍历,打印
NSLog("查询成功",false)
for(var i = 0;i < results.length; i++){
var girlObj = results[i]
NSLog(girlObj._id + ', ' + girlObj.girlName + ', ' + girlObj.girlAge + '岁, 「' + girlObj.girlDescription + '」, ' + girlObj.pubTime,false)
}
})
// 5.关闭数据库
connection.end()
效果如下:
更新一条记录node_37_update.js代码如下:
function NSLog(loli,needLogo=true) {console.log(loli);if(needLogo){console.log('\nCopyright © 2018 Powered by beyond')};}
// 1.导入框架
var mysql = require('mysql')
// 2.创建连接
var connection = mysql.createConnection({
host: 'localhost',
user: 'root',
password: '520',
database: 'db2'
})
// 3.连接数据库
connection.connect();
// 4.查询数据库
var sql = 'update girls set girlName = ?,girlAge = ?,girlDescription = ? where _id = ?'
var queryObj = {
'sql': sql,
timeout: 6000,
values: ['面码',13,'未闻花名','1']
}
connection.query(queryObj,function (error,results,fields) {
if(error){
return NSLog(error)
}
NSLog('更新成功')
})
// 5.关闭数据库
connection.end()
效果如下:
下面的node_37_poolFindAll.js 将演示一下
如何在Node中使用Mysql的 连接池 Pool Connection :
(因为每次查询都要连接然后断开的话 会非常消耗CPU资源)
(如果每一个用户连接后,又不断开,那么会不断地消耗内存资源)
官方示例代码如下:
npmjs.com/package/mysql#pooling-connections
node_37_poolFindAll.js代码如下:
function NSLog(loli,needLogo=true) {console.log(loli);if(needLogo){console.log('\nCopyright © 2018 Powered by beyond')};}
// 1.导入框架
var mysql = require('mysql')
// 2.创建连接
var pool = mysql.createPool({
connectionLimit: 10,
host: 'localhost',
user: 'root',
password: '123456',
database: 'db2'
})
// 3.直接查询数据库
var sql = 'select _id,girlName,girlAge,girlDescription,pubTime from girls'
pool.query(sql,function (error,results,fields) {
if(error){
return NSLog(error)
}
// 遍历,打印
NSLog("查询成功",false)
for(var i = 0;i < results.length; i++){
var girlObj = results[i]
NSLog(girlObj._id + ', ' + girlObj.girlName + ', ' + girlObj.girlAge + '岁, 「' + girlObj.girlDescription + '」, ' + girlObj.pubTime,false)
}
console.log("\nCopyright © 2018 Powered by beyond")
})
// 无需关闭,查询完毕会自动进行连接池 pool
效果如下:
下面把node_36_index.js这个使用MongoDB的CRUD项目,转换成使用Mysql的node_38_index.js项目
项目目录如下:
默认的views目录下的index目录下的3个html模板文件如下:
效果如下:
app入口文件node_38_index.js代码如下:
function NSLog(loli,needLogo=true) {console.log(loli);if(needLogo){console.log('\nCopyright © 2018 Powered by beyond')};}
// 导入框架
var express = require('express')
// 创建服务器对象
var appServer = express()
// 监听端口,并启动服务
appServer.listen(5267,function (error) {
if (error) {
return NSLog('启动失败: ' + error)
}
NSLog('服务启动成功')
})
// -----------------------------------
// 静态资源请求时的 staticFileUrlPrefix
var staticFileUrlPrefix = '/public/'
// var staticFileUrlPrefix = '/public'
// 访问也只能使用 localhost:5267/public/img/beyond.jpg
// 磁盘上的静态资源目录
var staticFilePath = './public/'
// var staticFilePath = 'public'
var callbackFunction = express.static(staticFilePath)
appServer.use(staticFileUrlPrefix,callbackFunction)
// -----------------------------------
// 指明:对于 所有后缀为html 的模板文件 使用模板引擎
var templateFileSuffix = 'html'
appServer.engine(templateFileSuffix,require('express-art-template'))
// 下面这一句参数配置,可有可无
appServer.set('view options',{
debug: process.env.NODE_ENV !== 'production'
})
// 注意:如果不想把模板文件放在默认的views目录下,则可以通过下面代码更改设置
// appServer.set('views','其他目录')
// -----------------------------------
// 使用middleware中间件body-parser进行post请求体中数据解析
var bodyParser = require('body-parser')
// 设置解析 application/x-www-form-urlencoded
appServer.use(bodyParser.urlencoded({extended: false}))
// 设置解析 application/json
appServer.use(bodyParser.json())
// -----------------------------------
// 自定义路由设计的目的是:
// 1.让主入口程序的职责更加单一,代码更加简洁
// 1.1 创建服务
// 1.2 做一些服务相关的配置,比如:
// 1.2.1 静态资源配置
// 1.2.2 模板引擎配置
// 1.2.3 body-parse 解析表单
// 1.2.4 挂载自定义路由
// 1.2.5 监听端口,启动服务
// 使用自定义的路由模块 必须使用./
// 注意: 配置模板引擎和body-parser, 一定要在挂载路由之前
var beyondRouter = require('./node_38_router')
appServer.use(beyondRouter)
路由模块代码node_38_router.js代码如下:
function NSLog(loli) {console.log(loli);return 'Copyright © 2018 Powered by beyond';};
/*
自定义路由模块的职责是:
专门处理所有的路由
根据不同的请求方式和路径,采取相应的处理方法
*/
// express 专门提供了路由的处理方法
var express = require('express')
// 1.使用express专门提供的路由器处理路由
var router = express.Router()
// ----------------引入dao模块-------------------
// 先对dao初始化
var poolDao = require('./node_38_dao')
// 时间格式化
var BeyondDateFormatFunction = require('./BeyondDateFormat')
// ----------------首页-------------------
router.get('/',function (request,response) {
// 至于请求参数可以这样:
// var queryObj = request.query
// ----------------查找所有------------------
var sql = 'select _id,girlName,girlAge,girlDescription,pubTime from girls'
poolDao.query(sql,function (error,results,fields) {
if(error){
return response.send(error)
}
// 使用模板引擎渲染
// 注意: 模板文件默认是放在views目录下
// 为此,我们在views目录下 分别为不同的业务模块创建了不同的文件夹
// 如 login登录 admin后台管理 index前台首页 article文章 comment评论
response.render('index/node_38_index.html',{girlArr:results})
})
// 无需关闭,查询完毕会自动进行连接池 pool
})
// ----------------添加的表单页面-------------------
router.get('/add',function (request,response) {
response.render('index/node_38_add.html')
})
// ----------------增加一条记录-------------------
router.post('/insert',function (request,response) {
// 1.body-parser得到obj
var girlObj = request.body
// 1.调用girlDao写到文件数据库
girlObj.pubTime = BeyondDateFormatFunction(new Date(),'yyyy-MM-dd')
// 2.保存到数据库
// 如果不指定字段名,则每一个都要填写值(主键可以用null代替)
var sql = 'insert into girls values(null,?,?,?,?)'
var queryObj = {
'sql': sql,
timeout: 6000,
values: [girlObj.girlName,girlObj.girlAge,girlObj.girlDescription,girlObj.pubTime]
}
poolDao.query(queryObj,function (error,results,fields) {
if(error){
// 有错误
return response.status(500).send(error)
}
// 没有错误,跳转到首页
response.redirect('/')
})
})
// ----------------修改页面-------------------
router.get('/edit',function (request,response) {
var sql = 'select _id,girlName,girlAge,girlDescription,pubTime from girls where _id = ?'
var queryObj = {
'sql': sql,
timeout: 6000,
values: [request.query._id]
}
poolDao.query(queryObj,function (error,results,fields) {
if(error){
// 如果出错
return response.status(500),send(error)
}
// 如果查询成功
response.render('index/node_38_edit.html',{'girl': results[0]})
})
})
// ----------------更新数据库-------------------
router.post('/update',function (request,response) {
// 1.请求体 id号 (前后多了两个引号,要手动去掉)
var girlID = request.body._id
var girlName = request.body.girlName
var girlAge = request.body.girlAge
var girlDescription = request.body.girlDescription
// 2.更新
var sql = 'update girls set girlName = ?,girlAge = ?,girlDescription = ? where _id = ?'
var queryObj = {
'sql': sql,
timeout: 6000,
values: [girlName,girlAge,girlDescription,girlID]
}
// 使用连接池更新记录
poolDao.query(queryObj,function (error,results,fields) {
if(error){
// 如果失败
return response.status(500).send(error)
}
// 如果成功
response.redirect('/')
})
})
// ----------------删除一条记录-------------------
router.get('/delete',function (request,response) {
// 1.获取query对象中的_id
var girlID = request.query._id
// 2.调用dao从数据库中删除一个对象
var sql = 'delete from girls where _id = ?'
var queryObj = {
'sql': sql,
timeout: 6000,
values: [girlID]
}
// 使用连接池
poolDao.query(queryObj,function (error,results,fields) {
if(error){
// 如果错误
return response.status(500).send(error)
}
// 如果没有错误,跳转到首页
return response.redirect('/')
})
})
// 3.在模块文件最后,导出router
module.exports = router
Dao数据库操作文件node_38_dao.js代码如下:
function NSLog(loli) {console.log(loli);return 'Copyright © 2018 Powered by beyond';};
// 1.导入框架
var mysql = require('mysql')
// 2.创建连接
var pool = mysql.createPool({
connectionLimit: 10,
host: 'localhost',
user: 'root',
password: '123456',
database: 'db2'
})
// 3.无需关闭,查询完毕会自动进行连接池 pool
// 4.导出连接池
module.exports = pool
三个html文件,代码与前一篇文章node_36_index(add和edit).html几乎一模一样,这儿就不重复显示了
回调地狱callback hell
先上一张经典的图:
演示读取失败时的node_39.js代码如下:
function NSLog(loli,needLogo=true) {console.log(loli);if(needLogo){console.log('\nCopyright © 2018 Powered by beyond')};}
// 回调地狱的解决方案演示 Promise
var fs = require('fs')
// 在ECMAScript6中,新增了一个API Promise
// Promise是一个构造函数,目的就是避免回调地狱 callback hell
// 通过new + 构造函数 创建一个Promise
// 构造函数的参数 是一个匿名函数(block代码块)
// Promise一旦创建,就会立即执行参数(即匿名函数block)里面的代码
NSLog('0_before_promise',false)
var promise_1 = new Promise(function () {
// Promise本身是同步顺序执行的,但是它里面的异步操作是异步执行的
NSLog('1_in_promise',false)
fs.readFile('node_39_1.txt','utf8',function (error,data) {
NSLog('3_in_promise_block',false)
if (error) {
// 如果失败,则承诺容器中的异步任务失败了
NSLog('读取失败')
}else{
// 如果成功,则承诺容器中的异步任务成功了
NSLog('读取成功: ' + data)
}
})
})
NSLog('2_after_promise',false)
效果如下:
读取成功时的效果如下:
注意: Promise容器中往往通过构造函数时的参数,传入一个匿名函数(block代码块), 这个block代码块会被立刻执行,
并且,这个block代码块中,往往封装的都是一些异步操作任务
Promise在初始状态时,只是Pending状态
一旦block代码块中的异步操作完成之时,Promise的状态就会发生改变.
当block代码块中的异步操作成功时,Promise状态变成Resolved
当block代码块中的异步操作失败时,Promise状态变成Rejected
示例图如下:
接下来要进入重头戏了,
下面给promise构造函数中的匿名函数(block代码块)写上成功时回调的参数1和失败时回调的参数2
并在异步任务完成时,回调对应的函数
上面的成功时回调的参数1和失败时回调的参数2 对应于 then方法的 参数1 和参数2
下面演示一下promise对象的then方法,以及then方法中的两个参数
node_40.js代码如下:
function NSLog(loli,needLogo=true) {console.log(loli);if(needLogo){console.log('\nCopyright © 2018 Powered by beyond')};}
// 回调地狱的解决方案演示 Promise
var fs = require('fs')
// 在ECMAScript6中,新增了一个API Promise
// Promise是一个构造函数,目的就是避免回调地狱 callback hell
// 通过new + 构造函数 创建一个Promise
// 构造函数的参数 是一个匿名函数(block代码块)
// Promise一旦创建,就会立即执行参数(即匿名函数block)里面的代码
// 匿名函数block有两个参数是用于回调的,它们分别是resolveCallBack和rejectCallback
// 对应于then方法的参数1和参数2
NSLog('0_before_promise',false)
var promise_1 = new Promise(function (resolveCallback,rejectCallback) {
// Promise本身是同步顺序执行的,但是它里面的异步操作是异步执行的
NSLog('1_in_promise',false)
fs.readFile('node_39_1.txt','utf8',function (error,successData) {
NSLog('5_in_promise_block',false)
if (error === null) {
// 如果成功,则承诺容器中的异步任务成功了
// 成功回调函数(即then方法的参数1)
resolveCallback(successData)
}else{
// 如果失败,则承诺容器中的异步任务失败了
// NSLog('读取失败' + error)
// 失败回调函数(即then方法的参数2)
rejectCallback(error)
}
})
})
NSLog('2_after_promise',false)
NSLog('3_before_then',false)
promise_1.then(function (successData) {
NSLog('6.读取成功: ' + successData)
},function (error) {
NSLog('6.读取失败: ' + error)
})
NSLog('4_after_then',false)
效果如下:
示意图如下:
关于 在then的参数(即匿名函数)中返回值 的说明
这个返回值类型 只有两种情况
第1种情况: 返回一个promise对象 (最常见)
那么下一个then中,参数1就是promise对象的resolveCallback
参数2就是promise对象的rejectCallback
第2种情况: 返回一个非Promise的对象 (极少用到)
比如返回一个String字符串 “未闻花名”
那么,在下一个then中就只有一个匿名函数callback作为参数
那个匿名函数callback中的参数 就是这儿的返回值 即String的值 “未闻花名”
下面再进一步演示一下,如果在then中返回一个字符串会怎么样
node_42.js代码如下:
function NSLog(loli,needLogo=true) {console.log(loli);if(needLogo){console.log('\nCopyright © 2018 Powered by beyond')};}
// 回调地狱的解决方案演示 Promise
var fs = require('fs')
// 在ECMAScript6中,新增了一个API Promise
// Promise是一个构造函数,目的就是避免回调地狱 callback hell
// 通过new + 构造函数 创建一个Promise
// 构造函数的参数 是一个匿名函数(block代码块)
// Promise一旦创建,就会立即执行参数(即匿名函数block)里面的代码
// 匿名函数block有两个参数是用于回调的,它们分别是resolveCallBack和rejectCallback
// 对应于then方法的参数1和参数2
NSLog('0_before_promise_1',false)
var promise_1 = new Promise(function (resolveCallback,rejectCallback) {
// Promise本身是同步顺序执行的,但是它里面的异步操作是异步执行的
NSLog('1_in_promise_1',false)
fs.readFile('node_40_1.txt','utf8',function (error,successData) {
if (error === null) {
// 如果成功,则承诺容器中的异步任务成功了
// 成功回调函数(即then方法的参数1)
resolveCallback(successData)
}else{
// 如果失败,则承诺容器中的异步任务失败了
// NSLog('读取失败' + error)
// 失败回调函数(即then方法的参数2)
rejectCallback(error)
}
})
})
NSLog('2_after_promise',false)
NSLog('3_before_then',false)
promise_1.then(function (successData) {
NSLog('5.读取txt成功: \n' + successData,false)
/*
关于这个返回值 要说明一下
这个返回值类型 只有两种情况
第1种情况: 返回一个promise对象
那么下一个then中,参数1就是promise对象的resolveCallback
参数2就是promise对象的rejectCallback
第2种情况: 返回一个非Promise的对象 (极少用到)
比如返回一个String字符串 "那朵花"
那么,在下一个then中就只有一个匿名函数callback作为参数
那个匿名函数callback中的参数 就是这儿的返回值 即String的值 "那朵花"
*/
return "未闻花名"
},function (error) {
NSLog('5.读取txt失败: \n' + error,false)
/*
关于这个返回值 要说明一下
这个返回值类型 只有两种情况
第1种情况: 返回一个promise对象
那么下一个then中,参数1就是promise对象的resolveCallback
参数2就是promise对象的rejectCallback
第2种情况: 返回一个非Promise的对象 (极少用到)
比如返回一个String字符串 "那朵花"
那么,在下一个then中就只有一个匿名函数callback作为参数
那个匿名函数callback中的参数 就是这儿的返回值 即String的值 "那朵花"
*/
return "anohana"
}).then(function (previousThenReturnData) {
NSLog('6.这个是上一个then中的返回值: \n ' + previousThenReturnData)
})
NSLog('4_after_then',false)
成功时效果如下:
失败时效果如下:
下面最核心最精华的部分了, 我们使用Promise的目的是避免回调地狱的同时,又要保证N个异步任务的结果,按顺序输出
比如下面的promise_1和promise_2和promise_3分别读取a,b,c三个txt方法,并按顺序输出其内容
在这儿我们就需要在promise_1的then方法中 输出了a.txt内容之后,return promise_2,
这样我们就可以继续调用promise_2的then方法中 输出b.txt的内容,
输出了b.txt内容之后同样的,我们返回promise_3对象,
这样我们就可以继续调用promise_3的then方法中 输出c.txt的内容
示例代码node_41.js如下:
function NSLog(loli,needLogo=true) {console.log(loli);if(needLogo){console.log('\nCopyright © 2018 Powered by beyond')};}
// 回调地狱的解决方案演示 Promise
var fs = require('fs')
// 在ECMAScript6中,新增了一个API Promise
// Promise是一个构造函数,目的就是避免回调地狱 callback hell
// 通过new + 构造函数 创建一个Promise
// 构造函数的参数 是一个匿名函数(block代码块)
// Promise一旦创建,就会立即执行参数(即匿名函数block)里面的代码
// 匿名函数block有两个参数是用于回调的,它们分别是resolveCallBack和rejectCallback
// 对应于then方法的参数1和参数2
NSLog('0_before_promise_1',false)
var promise_1 = new Promise(function (resolveCallback,rejectCallback) {
// Promise本身是同步顺序执行的,但是它里面的异步操作是异步执行的
NSLog('1_in_promise_1',false)
fs.readFile('node_40_1.txt','utf8',function (error,successData) {
if (error === null) {
// 如果成功,则承诺容器中的异步任务成功了
// 成功回调函数(即then方法的参数1)
resolveCallback(successData)
}else{
// 如果失败,则承诺容器中的异步任务失败了
// NSLog('读取失败' + error)
// 失败回调函数(即then方法的参数2)
rejectCallback(error)
}
})
})
var promise_2 = new Promise(function (resolveCallback,rejectCallback) {
// Promise本身是同步顺序执行的,但是它里面的异步操作是异步执行的
NSLog('2_in_promise_2',false)
fs.readFile('node_40_2.txt','utf8',function (error,successData) {
if (error === null) {
// 如果成功,则承诺容器中的异步任务成功了
// 成功回调函数(即then方法的参数1)
resolveCallback(successData)
}else{
// 如果失败,则承诺容器中的异步任务失败了
// NSLog('读取失败' + error)
// 失败回调函数(即then方法的参数2)
rejectCallback(error)
}
})
})
var promise_3 = new Promise(function (resolveCallback,rejectCallback) {
// Promise本身是同步顺序执行的,但是它里面的异步操作是异步执行的
NSLog('3_in_promise_3',false)
fs.readFile('node_40_3.txt','utf8',function (error,successData) {
if (error === null) {
// 如果成功,则承诺容器中的异步任务成功了
// 成功回调函数(即then方法的参数1)
resolveCallback(successData)
}else{
// 如果失败,则承诺容器中的异步任务失败了
// NSLog('读取失败' + error)
// 失败回调函数(即then方法的参数2)
rejectCallback(error)
}
})
})
NSLog('4_after_promise_3',false)
NSLog('5_before_then',false)
promise_1.then(function (successData) {
NSLog('10.读取1.txt成功: \n' + successData,false)
return promise_2
},function (error) {
NSLog('10.读取1.txt失败: \n' + error)
return promise_2
}).then(function (successData) {
NSLog('11.读取2.txt成功: \n' + successData,false)
return promise_3
},function (error) {
NSLog('11.读取2.txt失败: \n' + error)
return promise_3
}).then(function (successData) {
NSLog('12.读取3.txt成功: \n' + successData)
},function (error) {
NSLog('12.读取3.txt失败: \n' + error)
})
NSLog('6_after_then',false)
效果如下:
注意: 之所有 没有 7 8 9 ,是因为 三个block代码块中的异步操作 谁先完成 这个顺序是不确定的
示意图如下:
下面演示了自定义一个函数,参数是 文件路径,返回值是一个Promise
node_43.js代码如下:
function NSLog(loli,needLogo=true) {console.log(loli);if(needLogo){console.log('\nCopyright © 2018 Powered by beyond')};}
// 回调地狱的解决方案演示 Promise
var fs = require('fs')
// 自己封装一个readFile方法,参数是: filePath
// 返回值是: 一个promise
function beyondReadFile (filePath) {
// 在ECMAScript6中,新增了一个API Promise
// Promise是一个构造函数,目的就是避免回调地狱 callback hell
// 通过new + 构造函数 创建一个Promise
// 构造函数的参数 是一个匿名函数(block代码块)
// Promise一旦创建,就会立即执行参数(即匿名函数block)里面的代码
// 匿名函数block有两个参数是用于回调的,它们分别是resolveCallBack和rejectCallback
// 对应于then方法的参数1和参数2
var promise_1 = new Promise(function (resolveCallback,rejectCallback) {
// Promise本身是同步顺序执行的,但是它里面的异步操作是异步执行的
fs.readFile(filePath,'utf8',function (error,successData) {
if (error === null) {
// 如果成功,则承诺容器中的异步任务成功了
// 成功回调函数(即then方法的参数1)
resolveCallback(successData)
} else{
// 如果失败,则承诺容器中的异步任务失败了
// NSLog('读取失败' + error)
// 失败回调函数(即then方法的参数2)
rejectCallback(error)
}
})
})
// 返回promise,目的是为了能够链式使用then语法
return promise_1
}
// 调用一下,因为返回值是promise,所以可直接then
beyondReadFile('node_40_1.txt')
.then(function (successData) {
NSLog('读取1.txt成功:\n ' + successData)
// 见下一个示例
},function (error) {
NSLog('读取1.txt失败:\n ' + error)
})
效果如下:
下面是Promise的精华部分了,请看演示demo node_44.js代码如下:
function NSLog(loli,needLogo=true) {console.log(loli);if(needLogo){console.log('\nCopyright © 2018 Powered by beyond')};}
// 回调地狱的解决方案演示 Promise
var fs = require('fs')
// 自己封装一个readFile方法,参数是: filePath
// 返回值是: 一个promise
function beyondReadFile (filePath) {
// 在ECMAScript6中,新增了一个API Promise
// Promise是一个构造函数,目的就是避免回调地狱 callback hell
// 通过new + 构造函数 创建一个Promise
// 构造函数的参数 是一个匿名函数(block代码块)
// Promise一旦创建,就会立即执行参数(即匿名函数block)里面的代码
// 匿名函数block有两个参数是用于回调的,它们分别是resolveCallBack和rejectCallback
// 对应于then方法的参数1和参数2
var promise_1 = new Promise(function (resolveCallback,rejectCallback) {
// Promise本身是同步顺序执行的,但是它里面的异步操作是异步执行的
fs.readFile(filePath,'utf8',function (error,successData) {
if (error === null) {
// 如果成功,则承诺容器中的异步任务成功了
// 成功回调函数(即then方法的参数1)
resolveCallback(successData)
} else{
// 如果失败,则承诺容器中的异步任务失败了
// NSLog('读取失败' + error)
// 失败回调函数(即then方法的参数2)
rejectCallback(error)
}
})
})
// 返回promise,目的是为了能够链式使用then语法
return promise_1
}
// 调用一下,因为返回值是promise,所以可直接then
beyondReadFile('node_40_1.txt')
.then(function (successData1) {
NSLog('读取1.txt成功:\n ' + successData1,false)
// 下面是Promise最精彩的部分
var promise_2 = beyondReadFile('node_40_2.txt')
return promise_2
/*
关于这个返回值 要说明一下
这个返回值类型 只有两种情况
第1种情况: 返回一个promise对象
那么下一个then中,参数1就是promise对象的resolveCallback
参数2就是promise对象的rejectCallback
第2种情况: 返回一个非Promise的对象 (极少用到)
比如返回一个String字符串 "那朵花"
那么,在下一个then中就只有一个匿名函数callback作为参数
那个匿名函数callback中的参数 就是这儿的返回值 即String的值 "那朵花"
*/
},function (error1) {
NSLog('读取1.txt失败:\n ' + error1)
})
// 因为前面返回的是Promsise_2,所以可以接着then
.then(function (successData2) {
NSLog('读取2.txt成功:\n ' + successData2)
},function (error2) {
NSLog('读取2.txt失败:\n ' + error2)
})
效果如下:
依此类推,下面的node_45.js演示的是读取并按顺序输出3个文件,代码如下:
function NSLog(loli,needLogo=true) {console.log(loli);if(needLogo){console.log('\nCopyright © 2018 Powered by beyond')};}
// 回调地狱的解决方案演示 Promise
var fs = require('fs')
// 自己封装一个readFile方法,参数是: filePath
// 返回值是: 一个promise
function beyondReadFile (filePath) {
// 在ECMAScript6中,新增了一个API Promise
// Promise是一个构造函数,目的就是避免回调地狱 callback hell
// 通过new + 构造函数 创建一个Promise
// 构造函数的参数 是一个匿名函数(block代码块)
// Promise一旦创建,就会立即执行参数(即匿名函数block)里面的代码
// 匿名函数block有两个参数是用于回调的,它们分别是resolveCallBack和rejectCallback
// 对应于then方法的参数1和参数2
var promise_1 = new Promise(function (resolveCallback,rejectCallback) {
// Promise本身是同步顺序执行的,但是它里面的异步操作是异步执行的
fs.readFile(filePath,'utf8',function (error,successData) {
if (error === null) {
// 如果成功,则承诺容器中的异步任务成功了
// 成功回调函数(即then方法的参数1)
resolveCallback(successData)
} else{
// 如果失败,则承诺容器中的异步任务失败了
// NSLog('读取失败' + error)
// 失败回调函数(即then方法的参数2)
rejectCallback(error)
}
})
})
// 返回promise,目的是为了能够链式使用then语法
return promise_1
}
// 调用一下,因为返回值是promise,所以可直接then
beyondReadFile('node_40_1.txt')
.then(function (successData1) {
NSLog('读取1.txt成功:\n ' + successData1,false)
// 下面是Promise最精彩的部分
var promise_2 = beyondReadFile('node_40_2.txt')
return promise_2
/*
关于这个返回值 要说明一下
这个返回值类型 只有两种情况
第1种情况: 返回一个promise对象
那么下一个then中,参数1就是promise对象的resolveCallback
参数2就是promise对象的rejectCallback
第2种情况: 返回一个非Promise的对象 (极少用到)
比如返回一个String字符串 "那朵花"
那么,在下一个then中就只有一个匿名函数callback作为参数
那个匿名函数callback中的参数 就是这儿的返回值 即String的值 "那朵花"
*/
},function (error1) {
NSLog('读取1.txt失败:\n ' + error1)
})
// 因为前面返回的是Promsise_2,所以可以接着then
.then(function (successData2) {
NSLog('读取2.txt成功:\n ' + successData2,false)
// 下面是Promise最精彩的部分
var promise_3 = beyondReadFile('node_40_3.txt')
return promise_3
/*
关于这个返回值 要说明一下
这个返回值类型 只有两种情况
第1种情况: 返回一个promise对象
那么下一个then中,参数1就是promise对象的resolveCallback
参数2就是promise对象的rejectCallback
第2种情况: 返回一个非Promise的对象 (极少用到)
比如返回一个String字符串 "那朵花"
那么,在下一个then中就只有一个匿名函数callback作为参数
那个匿名函数callback中的参数 就是这儿的返回值 即String的值 "那朵花"
*/
},function (error2) {
NSLog('读取2.txt失败:\n ' + error2)
})
// 因为前面返回的是Promsise_2,所以可以接着then
.then(function (successData3) {
NSLog('读取3.txt成功:\n ' + successData3)
},function (error3) {
NSLog('读取3.txt失败:\n ' + error3)
})
效果如下:
整个效果如下:
解释说明如下:
下面演示一个Promise使用场景的示例demo,
在这个demo中,教程里面使用了json-server来提供api接口(因此,我们先来安装和使用json-server)
下面,我们先根据npmjs.com上面的json-server的官方文档,安装和使用json-server
npmjs.com/package/json-server
第1步, 安装命令如下:
sudo npm install –global json-server
效果如下:
第2步, 创建597_db.json,内容如下:
注意: 主键属性名必须叫id
{
"girls" : [
{
"id" : 1,
"girlName" : "面码",
"girlAge" : 13,
"animeId" : 1
},
{
"id" : 2,
"girlName" : "mathilda",
"girlAge" : 12,
"animeId" : 2
},
{
"id" : 3,
"girlName" : "逢坂大河",
"girlAge" : 16,
"animeId" : 3
},
{
"id" : 4,
"girlName" : "平泽唯",
"girlAge" : 14,
"animeId" : 4
}
],
"animes" : [
{
"id" : 1,
"animeName" : "未闻花名"
},
{
"id" : 2,
"animeName" : "这个杀手不太冷"
},
{
"id" : 3,
"animeName" : "龙与虎"
},
{
"id" : 4,
"animeName" : "轻音少女"
}
]
}
第3步, cd json所在目录,然后执行下面命令
json-server –watch 597_girls.json
效果如下:
第4步, 在地址栏输入 localhost:3000/girls 回车,请求接口时,返回的json结果如下:
输入localhost:3000/girls/1 回车,请求接口时,返回的json效果如下:
输入localhost:3000/animes 回车,请求接口时,返回的json效果如下:
教程中接下来又使用了一个命令
这个hs -c-l -o命令又是一个什么东东???
老规矩,先来安装一下 http-server
第1步, 使用以下命令
sudo npm install –global http-server
效果如下:
第2步, 启动http-server,输入
hs -c-l -o
效果如下:
官方说明如下: (意思是如果根目录下面没有public目录,就显示根目录文件列表)
http-server [path] [options]
[path] defaults to ./public if the folder exists, and ./ otherwise.
Now you can visit http://localhost:8080 to view your server
接下来安装模板引擎art-template
命令如下:
npm install art-template
效果如下:
接下来在597.html中使用模板引擎template-web.js
注意: json服务器的接口是 localhost:3000/girls/1
而http-server提供的服务器是 localhost:8080/597.html
我们在597.html中,使用596.html中自己封装的beyondGet方法,发送一个ajax请求 json服务器上的数据,
并且将返回的json字符串解析成对象,通过模板引擎,填充到form表单列表中
现在我们的597.html到底是要实现什么样的场景呢?
那就是: 发两次异步请求
第1次异步请求回girl的信息,其中girl对象中有一个属性animeId,记录她出演的动漫id
第2次异步请求回动漫列表
等两次请求相继完成后,我们再进行页面的渲染,并且在渲染的时候,会判断
如果girl对象的animeId属性 跟下拉的动漫列表 框中的id一致时,我们就让这个option默认为选中状态
下面是不用Promise的写法:
597.html代码如下:
<!DOCTPYE html>
<html lang="zh">
<head>
<link rel="icon" href="beyond2.jpg" type="image/x-icon"/>
<meta charset="UTF-8">
<meta name="author" content="beyond">
<meta http-equiv="refresh" content="520">
<meta name="description" content="未闻花名-免费零基础教程-beyond">
<meta name="viewport" content="width=device-width,
initial-scale=1.0, maximum-scale=1.0,minimum-scale=1.0,user-scalable=0" />
<meta name="keywords" content="HTML,CSS,JAVASCRIPT,JQUERY,XML,JSON,C,C++,C#,OC,PHP,JAVA,JSP,PYTHON,RUBY,PERL,LUA,SQL,LINUX,SHELL,汇编,日语,英语,泰语,韩语,俄语,粤语,阿语,魔方,乐理,动漫,PR,PS,AI,AE">
<title>beyond心中の动漫神作</title>
<link rel="stylesheet" type="text/css" href="beyondbasestylewhite5.css">
<script type="text/javascript" src="nslog.js"></script>
<!--[if lt IE 9]>
<script src="//apps.bdimg.com/libs/html5shiv/3.7/html5shiv.min.js"></script>
<script type="text/javascript" src="http://apps.bdimg.com/libs/jquery/1.10.2/jquery.js">
</script>
<![endif]-->
<style type="text/css">
body{
font-size: 100%;
/*声明margin和padding是个好习惯*/
margin: 0;
padding: 0;
background-image: url("sakura4.png");
background-repeat: no-repeat;
background-position: center center;
}
</style>
<!-- 绿色按钮的css效果 -->
<link rel="stylesheet" type="text/css" href="beyondbuttongreen.css">
<!-- 引入 jquery 2.1.4 -->
<!--[if gte IE 9]><!-->
<script type="text/javascript" src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.js">
</script>
<!--<![endif]-->
</head>
<body>
<h1 style="color:white;text-shadow:2px 2px 4px #000;letter-spacing:5px;" class="sgcontentcolor sgcenter">
未闻花名
</h1>
<!-- 渲染结果显示区域 -->
<form action="" id="id_form_renderResult" style="text-align:center;" >
</form>
<!-- 第1步,导入template-web.js -->
<script type="text/javascript" src="node_modules/art-template/lib/template-web.js"></script>
<!-- 第2步,定义模板引擎 -->
<!-- type只要 不是javascript即可,随便起,无意义
id 在第3步填充数据时用到
-->
<script type="text/beyondtemplate" id="art_template">
<div >
<label for="">芳名</label>
<input type="text" value="{{ girl.girlName }}"/>
</div>
<div >
<label for="">年龄</label>
<input type="text" value="{{ girl.girlAge }}"/>
</div>
<div>
<label for="">主演动漫</label>
<select>
{{ each animes }}
{{ if girl.animeId === $value.id }}
<option value="{{ $value.id }}" selected> {{ $value.animeName }} </option>
{{ else }}
<option value="{{ $value.id }}"> {{ $value.animeName }} </option>
{{ /if }}
{{ /each }}
</select>
</div>
</script>
<button id="id_btn" class="class_btn class_btn_green" type="button" style="display:block;margin:auto;margin-top:10px;">点我试试</button>
<script type="text/javascript">
// 自己封装一个get请求
function beyondGet (url,callback) {
// 自己实现一个ajax请求
// 1. 创建xhr
var xmlHttpRequest = new XMLHttpRequest()
// 2. 请求url资源
xmlHttpRequest.onload = function () {
// 5. 核心代码,回调结果
callback(xmlHttpRequest.responseText)
}
// 3.连接资源
xmlHttpRequest.open("get",url,true)
// 4.发送请求
xmlHttpRequest.send()
}
$(document).ready(function () {
$(".class_btn_green").click(function () {
// 使用自己封装的ajax请求
// 第1个接口, 获取girl的数据
beyondGet("http://127.0.0.1:3000/girls/2",function (responseGirl) {
var girlObj = JSON.parse(responseGirl)
// 第2个接口,获取所有的动漫列表接口
beyondGet('http://127.0.0.1:3000/animes',function (responseAnimes) {
var animeArr = JSON.parse(responseAnimes)
// <!-- 第3步,给模板引擎绑定数据 -->
// 注意: 函数名,必须是template
// 参数1: 模板的id
// 参数2: 要填充的数据封装成的 对象
var resultHTML = template('art_template',{
girl: girlObj,
animes: animeArr
})
// 这是干嘛:将渲染后的字符串,显示到界面上
document.querySelector('#id_form_renderResult').innerHTML = resultHTML
NSLog('resultHTML: \n' + resultHTML)
})
})
})
})
</script>
<p class="sgcenter">
<b>注意:</b>模拟Promise使用场景<br/>
json-server提供API服务(3000端口)<br/>
http-server提供Web服务(8080端口)<br/>
封装了XMLHttpRequest.onload方法<br/>
art-template的template-web.js渲染
</p>
<footer id="copyright">
<p style="font-size:14px;text-align:center;font-style:italic;">
Copyright © <a id="author">2018</a> Powered by <a id="author">beyond</a>
</p>
</footer>
</body>
</html>
效果如下:
下面的598.html中使用的是jQuery的get方法(支持Promise),代码如下:
注意$.get(urlPath)只有一个参数时,默认返回的是一个Promise
<!DOCTPYE html>
<html lang="zh">
<head>
<link rel="icon" href="beyond2.jpg" type="image/x-icon"/>
<meta charset="UTF-8">
<meta name="author" content="beyond">
<meta http-equiv="refresh" content="520">
<meta name="description" content="未闻花名-免费零基础教程-beyond">
<meta name="viewport" content="width=device-width,
initial-scale=1.0, maximum-scale=1.0,minimum-scale=1.0,user-scalable=0" />
<meta name="keywords" content="HTML,CSS,JAVASCRIPT,JQUERY,XML,JSON,C,C++,C#,OC,PHP,JAVA,JSP,PYTHON,RUBY,PERL,LUA,SQL,LINUX,SHELL,汇编,日语,英语,泰语,韩语,俄语,粤语,阿语,魔方,乐理,动漫,PR,PS,AI,AE">
<title>beyond心中の动漫神作</title>
<link rel="stylesheet" type="text/css" href="beyondbasestylewhite5.css">
<script type="text/javascript" src="nslog.js"></script>
<!--[if lt IE 9]>
<script src="//apps.bdimg.com/libs/html5shiv/3.7/html5shiv.min.js"></script>
<script type="text/javascript" src="http://apps.bdimg.com/libs/jquery/1.10.2/jquery.js">
</script>
<![endif]-->
<style type="text/css">
body{
font-size: 100%;
/*声明margin和padding是个好习惯*/
margin: 0;
padding: 0;
background-image: url("sakura4.png");
background-repeat: no-repeat;
background-position: center center;
}
</style>
<!-- 绿色按钮的css效果 -->
<link rel="stylesheet" type="text/css" href="beyondbuttongreen.css">
<!-- 引入 jquery 2.1.4 -->
<!--[if gte IE 9]><!-->
<script type="text/javascript" src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.js">
</script>
<!--<![endif]-->
</head>
<body>
<h1 style="color:white;text-shadow:2px 2px 4px #000;letter-spacing:5px;" class="sgcontentcolor sgcenter">
未闻花名
</h1>
<!-- 渲染结果显示区域 -->
<form action="" id="id_form_renderResult" style="text-align:center;" >
</form>
<!-- 第1步,导入template-web.js -->
<script type="text/javascript" src="node_modules/art-template/lib/template-web.js"></script>
<!-- 第2步,定义模板引擎 -->
<!-- type只要 不是javascript即可,随便起,无意义
id 在第3步填充数据时用到
-->
<script type="text/beyondtemplate" id="art_template">
<div >
<label for="">芳名</label>
<input type="text" value="{{ girl.girlName }}"/>
</div>
<div >
<label for="">年龄</label>
<input type="text" value="{{ girl.girlAge }}"/>
</div>
<div>
<label for="">主演动漫</label>
<select>
{{ each animes }}
{{ if girl.animeId === $value.id }}
<option value="{{ $value.id }}" selected> {{ $value.animeName }} </option>
{{ else }}
<option value="{{ $value.id }}"> {{ $value.animeName }} </option>
{{ /if }}
{{ /each }}
</select>
</div>
</script>
<button id="id_btn" class="class_btn class_btn_green" type="button" style="display:block;margin:auto;margin-top:10px;">点我试试</button>
<script type="text/javascript">
var girlObj = null
$(document).ready(function () {
$(".class_btn_green").click(function () {
// 使用自己封装的ajax请求
// 第1个接口, 获取girl的数据
// get方法只有一个参数时,返回的就是一个Promise
$.get("http://127.0.0.1:3000/girls/2")
.then(function (responseGirlObj) {
// 因为第2个接口中拿不到,所以使用全局变量
girlObj = responseGirlObj
// 返回一个Promise
// 第2个接口,获取所有的动漫列表接口
$.get('http://127.0.0.1:3000/animes')
})
.then(function (responseAnimes) {
var animeArr = responseAnimes
// <!-- 第3步,给模板引擎绑定数据 -->
// 注意: 函数名,必须是template
// 参数1: 模板的id
// 参数2: 要填充的数据封装成的 对象
var resultHTML = template('art_template',{
girl: girlObj,
animes: animeArr
})
// 这是干嘛:将渲染后的字符串,显示到界面上
document.querySelector('#id_form_renderResult').innerHTML = resultHTML
NSLog('resultHTML: \n' + resultHTML)
})
})
})
</script>
<p class="sgcenter">
<b>注意:</b>模拟Promise使用场景<br/>
json-server提供API服务(3000端口)<br/>
http-server提供Web服务(8080端口)<br/>
jQuery的get方法 默认 支持Promise<br/>
art-template的template-web.js渲染
</p>
<footer id="copyright">
<p style="font-size:14px;text-align:center;font-style:italic;">
Copyright © <a id="author">2018</a> Powered by <a id="author">beyond</a>
</p>
</footer>
</body>
</html>
效果如下:
下面的599.html中使用的是自己封装的Get + Promise方法,代码如下:
<!DOCTPYE html>
<html lang="zh">
<head>
<link rel="icon" href="beyond2.jpg" type="image/x-icon"/>
<meta charset="UTF-8">
<meta name="author" content="beyond">
<meta http-equiv="refresh" content="520">
<meta name="description" content="未闻花名-免费零基础教程-beyond">
<meta name="viewport" content="width=device-width,
initial-scale=1.0, maximum-scale=1.0,minimum-scale=1.0,user-scalable=0" />
<meta name="keywords" content="HTML,CSS,JAVASCRIPT,JQUERY,XML,JSON,C,C++,C#,OC,PHP,JAVA,JSP,PYTHON,RUBY,PERL,LUA,SQL,LINUX,SHELL,汇编,日语,英语,泰语,韩语,俄语,粤语,阿语,魔方,乐理,动漫,PR,PS,AI,AE">
<title>beyond心中の动漫神作</title>
<link rel="stylesheet" type="text/css" href="beyondbasestylewhite5.css">
<script type="text/javascript" src="nslog.js"></script>
<!--[if lt IE 9]>
<script src="//apps.bdimg.com/libs/html5shiv/3.7/html5shiv.min.js"></script>
<script type="text/javascript" src="http://apps.bdimg.com/libs/jquery/1.10.2/jquery.js">
</script>
<![endif]-->
<style type="text/css">
body{
font-size: 100%;
/*声明margin和padding是个好习惯*/
margin: 0;
padding: 0;
background-image: url("sakura4.png");
background-repeat: no-repeat;
background-position: center center;
}
</style>
<!-- 绿色按钮的css效果 -->
<link rel="stylesheet" type="text/css" href="beyondbuttongreen.css">
<!-- 引入 jquery 2.1.4 -->
<!--[if gte IE 9]><!-->
<script type="text/javascript" src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.js">
</script>
<!--<![endif]-->
</head>
<body>
<h1 style="color:white;text-shadow:2px 2px 4px #000;letter-spacing:5px;" class="sgcontentcolor sgcenter">
未闻花名
</h1>
<!-- 渲染结果显示区域 -->
<form action="" id="id_form_renderResult" style="text-align:center;" >
</form>
<!-- 第1步,导入template-web.js -->
<script type="text/javascript" src="node_modules/art-template/lib/template-web.js"></script>
<!-- 第2步,定义模板引擎 -->
<!-- type只要 不是javascript即可,随便起,无意义
id 在第3步填充数据时用到
-->
<script type="text/beyondtemplate" id="art_template">
<div >
<label for="">芳名</label>
<input type="text" value="{{ girl.girlName }}"/>
</div>
<div >
<label for="">年龄</label>
<input type="text" value="{{ girl.girlAge }}"/>
</div>
<div>
<label for="">主演动漫</label>
<select>
{{ each animes }}
{{ if girl.animeId === $value.id }}
<option value="{{ $value.id }}" selected> {{ $value.animeName }} </option>
{{ else }}
<option value="{{ $value.id }}"> {{ $value.animeName }} </option>
{{ /if }}
{{ /each }}
</select>
</div>
</script>
<button id="id_btn" class="class_btn class_btn_green" type="button" style="display:block;margin:auto;margin-top:10px;">点我试试</button>
<script type="text/javascript">
// 自己封装一个get请求 (返回Promise)
function beyondGet (url,callBack) {
var promise = new Promise(function (resolveCallback,rejectCallback) {
// 异步请求
// 自己实现一个ajax请求
// 1. 创建xhr
var xmlHttpRequest = new XMLHttpRequest()
// 2. 请求url资源
xmlHttpRequest.onload = function () {
// 5. 核心代码,回调结果
// 既可以在参数中传入callBack,使用回调模式,又可以不传而使用.then形式
// 当用户不传callBack时,使用.then形式
callBack && callBack(xmlHttpRequest.responseText)
resolveCallback(JSON.parse(xmlHttpRequest.responseText))
}
xmlHttpRequest.onerror = function (error) {
// 5.核心代码,回调结果
rejectCallback(error)
}
// 3.连接资源
xmlHttpRequest.open("get",url,true)
// 4.发送请求
xmlHttpRequest.send()
})
// 核心代码,返回promise
return promise
}
var girlObj = null
$(document).ready(function () {
$(".class_btn_green").click(function () {
// 使用自己封装的ajax请求
// 第1个接口, 获取girl的数据
// 自定义的get方法 返回的就是一个Promise
beyondGet("http://127.0.0.1:3000/girls/2")
.then(function (responseGirlObj) {
// 因为第2个接口中拿不到,所以使用全局变量
girlObj = responseGirlObj
// 返回一个Promise
// 第2个接口,获取所有的动漫列表接口
var promise_2 = beyondGet('http://127.0.0.1:3000/animes')
return promise_2
})
.then(function (responseAnimes) {
var animeArr = responseAnimes
// <!-- 第3步,给模板引擎绑定数据 -->
// 注意: 函数名,必须是template
// 参数1: 模板的id
// 参数2: 要填充的数据封装成的 对象
var resultHTML = template('art_template',{
girl: girlObj,
animes: animeArr
})
// 这是干嘛:将渲染后的字符串,显示到界面上
document.querySelector('#id_form_renderResult').innerHTML = resultHTML
NSLog('resultHTML: \n' + resultHTML)
})
})
})
</script>
<p class="sgcenter">
<b>注意:</b>模拟Promise使用场景<br/>
json-server提供API服务(3000端口)<br/>
http-server提供Web服务(8080端口)<br/>
自己封装Get方法 (return Promise)<br/>
既可以在Get参数中 传入callBack形式<br/>
又可不传callBack参数,使用.then形式<br/>
art-template的template-web.js渲染
</p>
<footer id="copyright">
<p style="font-size:14px;text-align:center;font-style:italic;">
Copyright © <a id="author">2018</a> Powered by <a id="author">beyond</a>
</p>
</footer>
</body>
</html>
效果如下:
最后,我们把前面的mongoose案例node_36_router.js这个文件
全部改写成return Promise 加 .then调用形式
(另外的node_36_index.js和node_36_dao.js以及3个html文件 几乎都不用动)
改写后的代码node_47_router.js代码如下:
function NSLog(loli) {console.log(loli);return 'Copyright © 2018 Powered by beyond';};
/*
自定义路由模块的职责是:
专门处理所有的路由
根据不同的请求方式和路径,采取相应的处理方法
*/
// express 专门提供了路由的处理方法
var express = require('express')
// 1.使用express专门提供的路由器处理路由
var router = express.Router()
// ----------------引入dao模块-------------------
// 先对dao初始化
var GirlDaoFunction = require('./node_47_dao')
// 时间格式化
var BeyondDateFormatFunction = require('./BeyondDateFormat')
// ----------------首页-------------------
router.get('/',function (request,response) {
// 至于请求参数可以这样:
// var queryObj = request.query
// ----------------查找所有------------------
// 查找所有,第1个参数不写,就是查找所有
GirlDaoFunction.find()
// find 方法返回的是:promise
// promise的then方法的参数1是 resolveCallback
// promise的then方法的参数2是 rejectCallback
.then(function (girls) {
// then方法的参数1是: resolveCallback
// 使用模板引擎渲染
// 注意: 模板文件默认是放在views目录下
// 为此,我们在views目录下 分别为不同的业务模块创建了不同的文件夹
// 如 login登录 admin后台管理 index前台首页 article文章 comment评论
response.render('index/node_47_index.html',{girlArr:girls})
},function (error) {
// then方法的参数2是: rejectCallback
if (error) {
return NSLog('查询出错: ' + error)
}
})
})
// ----------------添加的表单页面-------------------
router.get('/add',function (request,response) {
response.render('index/node_47_add.html')
})
// ----------------增加一条记录-------------------
router.post('/insert',function (request,response) {
// 1.body-parser得到obj
var girlObj = request.body
// NSLog(girlObj)
// 1.调用girlDao写到文件数据库
girlObj.pubTime = BeyondDateFormatFunction(new Date(),'yyyy-MM-dd')
var newGirl = new GirlDaoFunction(girlObj)
// 2.保存到数据库
newGirl.save()
// save 方法返回的是:promise
// promise的then方法的参数1是 resolveCallback
// promise的then方法的参数2是 rejectCallback
.then(function () {
// then方法的参数1是: resolveCallback
// 没有错误,跳转到首页
response.redirect('/')
},function(error) {
// then方法的参数2是: rejectCallback
if (error) {
// 有错误
return response.status(500).send(error)
}
})
})
// ----------------修改页面-------------------
router.get('/edit',function (request,response) {
// 查询的girlID 不知怎么滴,首尾有引号
// "5ad42675f917fa32e250a58a"
// 使用正则,把引号去掉, 如果不使用g的话,仅仅只是去掉第1个引号
var pureId = request.query._id.replace(/"/g,"")
// NSLog('query: ' + pureId)
GirlDaoFunction.findById(pureId)
// findById 方法返回的是:promise
// promise的then方法的参数1是 resolveCallback
// promise的then方法的参数2是 rejectCallback
.then(function (girl) {
// then方法的参数1是: resolveCallback
response.render('index/node_47_edit.html',{'girl': girl})
},function (error) {
// then方法的参数2是: rejectCallback
if (error) {
return response.status(500),send(error)
}
})
})
// ----------------更新数据库-------------------
router.post('/update',function (request,response) {
// 1.请求体 id号 (前后多了两个引号,要手动去掉)
var girlID = request.body._id
var girlIDWithoutQuote = girlID.replace(/"/g,'')
// 2.重新设置回去
request.body._id = girlIDWithoutQuote
// 3.根据id查询和更新
GirlDaoFunction.findByIdAndUpdate(girlIDWithoutQuote,request.body)
// findByIdAndUpdate 方法返回的是:promise
// promise的then方法的参数1是 resolveCallback
// promise的then方法的参数2是 rejectCallback
.then(function () {
// then方法的参数1是: resolveCallback
// 如果保存成功,回首页
response.redirect('/')
},function() {
// then方法的参数2是: rejectCallback
if(error){
// 如果保存出错了
return response.status(500).send(error)
}
})
})
// ----------------删除一条记录-------------------
router.get('/delete',function (request,response) {
// 1.获取query对象中的_id
var girlID = request.query._id
var girlIDWithoutQuote = girlID.replace(/"/g,'')
// 2.调用dao从数据库中删除一个对象
GirlDaoFunction.findByIdAndRemove(girlIDWithoutQuote)
// findByIdAndRemove 方法返回的是:promise
// promise的then方法的参数1是 resolveCallback
// promise的then方法的参数2是 rejectCallback
.then(function () {
// then方法的参数1是: resolveCallback
// 如果没有错误,跳转到首页
return response.redirect('/')
},function(error) {
// then方法的参数2是: rejectCallback
if (error) {
// 有错误
return response.status(500).send(error)
}
})
})
// 3.在模块文件最后,导出router
module.exports = router
效果如下: (跟node_36_router.js效果是一样的)
附上原来的使用callback回调函数作参数的写法的node_36_router.js对比一下
function NSLog(loli) {console.log(loli);return 'Copyright © 2018 Powered by beyond';};
/*
自定义路由模块的职责是:
专门处理所有的路由
根据不同的请求方式和路径,采取相应的处理方法
*/
// express 专门提供了路由的处理方法
var express = require('express')
// 1.使用express专门提供的路由器处理路由
var router = express.Router()
// ----------------引入dao模块-------------------
// 先对dao初始化
var GirlDaoFunction = require('./node_36_dao')
// 时间格式化
var BeyondDateFormatFunction = require('./BeyondDateFormat')
// ----------------首页-------------------
router.get('/',function (request,response) {
// 至于请求参数可以这样:
// var queryObj = request.query
// ----------------查找所有------------------
// 查找所有,第1个参数不写,就是查找所有
GirlDaoFunction.find(function (error,girls) {
if (error) {
return NSLog('查询出错: ' + error)
}
// 使用模板引擎渲染
// 注意: 模板文件默认是放在views目录下
// 为此,我们在views目录下 分别为不同的业务模块创建了不同的文件夹
// 如 login登录 admin后台管理 index前台首页 article文章 comment评论
response.render('index/node_36_index.html',{girlArr:girls})
})
})
// ----------------添加的表单页面-------------------
router.get('/add',function (request,response) {
response.render('index/node_36_add.html')
})
// ----------------增加一条记录-------------------
router.post('/insert',function (request,response) {
// 1.body-parser得到obj
var girlObj = request.body
// NSLog(girlObj)
// 1.调用girlDao写到文件数据库
girlObj.pubTime = BeyondDateFormatFunction(new Date(),'yyyy-MM-dd')
var newGirl = new GirlDaoFunction(girlObj)
// 2.保存到数据库
newGirl.save(function (error) {
if (error) {
// 有错误
return response.status(500).send(error)
}
// 没有错误,跳转到首页
response.redirect('/')
})
})
// ----------------修改页面-------------------
router.get('/edit',function (request,response) {
// 查询的girlID 不知怎么滴,首尾有引号
// "5ad42675f917fa32e250a58a"
// 使用正则,把引号去掉, 如果不使用g的话,仅仅只是去掉第1个引号
var pureId = request.query._id.replace(/"/g,"")
// NSLog('query: ' + pureId)
GirlDaoFunction.findById(pureId,function (error,girl) {
if (error) {
return response.status(500),send(error)
}
response.render('index/node_36_edit.html',{'girl': girl})
})
})
// ----------------更新数据库-------------------
router.post('/update',function (request,response) {
// 1.请求体 id号 (前后多了两个引号,要手动去掉)
var girlID = request.body._id
var girlIDWithoutQuote = girlID.replace(/"/g,'')
// 2.重新设置回去
request.body._id = girlIDWithoutQuote
// 3.根据id查询和更新
GirlDaoFunction.findByIdAndUpdate(girlIDWithoutQuote,request.body,function (error) {
if(error){
// 如果保存出错了
return response.status(500).send(error)
}
// 如果保存成功,回首页
response.redirect('/')
})
})
// ----------------删除一条记录-------------------
router.get('/delete',function (request,response) {
// 1.获取query对象中的_id
var girlID = request.query._id
var girlIDWithoutQuote = girlID.replace(/"/g,'')
// 2.调用dao从数据库中删除一个对象
GirlDaoFunction.findByIdAndRemove(girlIDWithoutQuote,function (error) {
if (error) {
// 有错误
return response.status(500).send(error)
}
// 如果没有错误,跳转到首页
return response.redirect('/')
})
})
// 3.在模块文件最后,导出router
module.exports = router
未完待续,下一章节,つづく