回调函数 Promise

本文探讨了JavaScript中的异步编程,重点介绍了回调函数及其在处理异步操作时的问题,如回调地狱。随后引入Promise的概念,解释了Promise作为解决回调问题的机制,如何通过提供统一API来管理异步操作。文中还展示了Promise的基本语法和使用场景,并通过HTTP请求和用户数据获取的例子,说明Promise如何简化异步编程,避免回调函数的深度嵌套。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

异步编程

回调函数

不成立的情况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 () { })
    }
})
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值