回调函数
函数也是一种数据类型,既可以做参数,又可以充当返回值
传统的函数
直接通过return 返回结果
function add (x,y){
return x+y
}
add(10,20)
异步函数
函数封装了异步的操作
异步操作:
setTimeout
setInterval
ajax
node中的文件操作
一般异步API都伴随一个回调函数
function add (x,y){
setTimeout(() => {
var ret = x + y
},1000)
}
add(10,20)
//思考:把函数的结果ret弄出来
错误思路
1.return (错误,实现不了)
2.使用全局变量存储(变量在全局,封装没意义了)
function add (x,y){
setTimeout(() => {
var ret = x + y
// return ret 错,此处的return只能返回给延时器里的函数
},1000)
//return ret 错 拿不到延时器里的ret
}
add(10,20)
var ret
function add (x,y){
setTimeout(() => {
ret = x + y
},1000)
}
add(10,20)
setTimeout(() =>{
console.log(ret)
},1000)
正确思路 : 把回调函数做参数
目的1 : 获取函数内部的异步操作结果
function add (x,y,callback){
setTimeout( () => {
var ret = x + y
callback(ret)
},1000)
}
add(10,20,ret => {
console.log(ret)
})
目的2 : 在异步操作完成后再调用函数
function fn (callback){
setInterval( () => {
callback()
},1000 )
}
fn( () => {
//定时器结束后,执行的回调函数
console.log('定时器结束后,要做的任务')
} )
回调地狱
多层异步嵌套
- 异步(不嵌套)无法控制顺序( 缺点 : 异步的执行顺序不固定)
fs.readFile('a.txt', 'utf8',(err,data) =>{
if(err){
throw err
}
console.log(data)
})
fs.readFile('b.txt', 'utf8',(err,data) =>{
if(err){
throw err
}
console.log(data)
})
fs.readFile('c.txt', 'utf8',(err,data) =>{
if(err){
throw err
}
console.log(data)
})
-
通过回调嵌套方式 控制顺序
会出现新的问题 : 回调地狱
fs.readFile('a.txt', 'utf8',(err,data) =>{
if(err){
throw err
}
console.log(data)
fs.readFile('b.txt', 'utf8',(err,data) =>{
if(err){
throw err
}
console.log(data)
fs.readFile('c.txt', 'utf8',(err,data) =>{
if(err){
throw err
}
console.log(data)
})
})
})
Promise
为什么用Promise
使用
Promise
来解决 异步编程回调(传统的异步编程是 回调函数+事件)
什么是Promise
以 同步的方式编写代码 来 处理异步操作的一种 解决方案
-
特点 :
-
对象的状态不受外界影响
只有异步操作的结果,可以决定当前是哪一种状态,任何操作都无法改变这个状态
pending
: 初始值 ,表示进行中fulfilled
: 表示操作成功rejected
: 表示操作失败
-
-
一旦状态改变,就不会再变
pending ---> fulfilled
pending ---> rejected
- ,只要这两种情况发生,就称为
resolved(已定型)
-
优点
- 把异步操作以同步操作的流程表达出来,避免层层嵌套的回调函数
- Promise对象提供了接口,操作更容易
-
缺点
- 无法取消Promise(一旦创建后就会立即执行,中途无法取消)
- 如果不设置回调,Promise内部抛出的错误,不会反应到外部
- 当处于pending状态时,无法得知当前是哪一阶段
Promise基本使用
使用步骤
- 使用
new Promise(callback)
或者快捷方式Promise.resolve()
Promise.reject()
- 在
callback
中指定要处理的异步操作- 处理成功 ,调用
resolve
- 处理失败 ,调用
reject
- 处理成功 ,调用
// resolve : 把Promise对象的状态 从 pending--> resolved,并把异步操作成功的结果传递出去
// reject : 把Promise对象的状态 从 pending--> rejected,并返回异步操作报出的错误信息
const promise = new Promise( (resolve,reject) => {
if(/*异步操作成功判断*/){
resolve(data)
}else{
reject(err)
}
})
//如果promise实例的状态发生改变,就会触发then方法绑定的回调
promise.then(data => {
console.log(data) //111
},err => {
console.log(err) //222
})
promise的执行顺序
promise本身是同步的, 而promise内部有异步方法
//异步 --- 宏任务(下次循环执行的)
setTimeout( () => {
console.log('setTimeout')
},1000 )
const promise = new Promise( (resolve,reject) => {
console.log('promise')
resolve()
})
//异步 ---- 微任务 (本次循环执行的)
promise.then( () => {
console.log('promise1')
})
console.log('outer')
//promise
//outer
//promise1
//setTimeout
补充 : 宏任务和微任务
macro-task(宏任务):包括整体代码script,setTimeout,setInterval
micro-task(微任务):Promise,process.nextTick
先执行宏任务(下一次循环执行) , 再执行微任务 (本次循环执行)
Promise的API
.then
语法:
Promise.prototype.then(onFulfilled, onRejected)
onFullfilled
: promise调用resolve()后触发该回调
onRejected
: promise调用reject()后触发该回调作用 : 为Promise实例添加状态改变时的回调函数
返回值 : 返回新的promise实例
//把异步操作封装起来
function timeout(txt) {
return new Promise( (resolve,rejecte) => {
setTimeout ( () => resolve(txt) ,1000 )
} )
}
timeout('111')
.then( data => {
console.log(data) //111
return timeout('222')
})
.then (data => {
console.log(data) //在111输出后延迟1秒 后输出 222
return timeout('333')
})
.then (data => {
console.log(data) //延迟2秒后 输出333
return ....//可以继续嵌套
})
.catch
语法:
Promise.prototype.catch(onRejected)
onRejected
:发生错误时触发的回调
//封装ajax
const p = new Promise( (resolve,reject) => {
$.ajax({
url:'xxx',
data:'xxx',
success:resolve,
error : reject
})
})
p.then(res => {}).catch(err => {})
.all
语法:
Promise.all(iterable)
作用 : 将多个Promise实例,包装成一个新的 v实例
多个Promise实例是同时开始,并行执行的,而不是顺序执行的
多个异步 都 完成才执行then
function timeouts(timer){
return new Promise( (resolve,reject) => {
setTimeout( () => {resolve(timer)},1000)
})
}
let startTime = Date.now()
Promise.all( [
timeouts(1),
timeouts(500),
timeouts(1000),
timeouts(1500),
]).then( res => {
console.log(Date.now() - startTime + 'ms')
console.log(res)
})
//1501ms
//[ 1, 500, 1000, 1500 ]
.race
语法 :
Promise.race(iterable)
作用 : 也是将多个Promise实例 ,包装成一个新的Promise实例
只要有一个异步完成,就调用then,通俗的讲,就是谁跑的快,就返回谁,
function timeouts(timer){
return new Promise( (resolve,reject) => {
setTimeout( () => {resolve(timer)},1000)
})
}
let startTime = Date.now()
Promise.race( [
timeouts(2000),
timeouts(500),
timeouts(1000),
timeouts(1500),
]).then( res => {
console.log(Date.now() - startTime + 'ms')
console.log(res)
})
//507ms
//500
.resolve
语法 :
Promise.resolve()
作用 : 将参数 转成一个promise对象, resolve状态
Promise.resolve('success').then( res => {
console.log(res) //success
})
//等同于
new Promise( (resolve,reject) => {
resolve('success')
}).then( res => {
console.log(res) //success
})
.reject
语法 :
Promise.reject()
作用 : 将参数 转成一个promise对象, reject状态
Promise.reject('aaa').then( res =>{
console.log(res)
}).catch(err => {
console.log(err) //aaa
})
Async/await
和
promise
协同工作
Async
async : 异步
作用 : 函数总是返回一个promise对象,如果代码中有
return <非promise>
语句,JavaScript会自动把 返回的值 包装成promise的resolved值注意 : 要放在函数的前面
async function f(){}
async function f(){
return 1
}
f().then(alert) //1
async function f() {
return Promise.resolve(1)
}
f().then(alert) // 1
Await
await : 等待
作用 : 等待一个异步操作的结果(promise实例)
注意 : 必须和
async
配合使用await 异步操作
async function f(){
let p = await new Promise( (resolve,reject) => {
setTimeout(resolve,3000)
})
console.log(p)
}
注意点
async
和await
必须成对出现
async function fn(){
let res = await /*异步操作*/
console.log(res) //异步操作的结果
}
async
一定要加到await
最近的函数上面
function fn(){
async function f(){
let res = await /*异步操作*/
console.log(res) //异步操作的结果
}
}
- 使用
try....catch
处理异常
async function f(){
try{
let res = await /*异步操作*/
console.log(res) //异步操作的结果
}catch(error){
console.log('异步操作失败')
}
}
async/await 和 promise.then/.catch
- 使用
async/await
来代替.then/.catch
,更方便 - 特例, 当在
async
外部处理异步时,只能使用.then/.catch