一、什么是回调
程序里面的任务可以根据执行顺序不同分为同步任务和异步任务两种。
- 同步任务是指那些没有被引擎挂起的,在主线程上排队执行的任务,只有前一个任务执行完毕,后一个任务才会执行,如:
function fn1(){
console.log(1)
}
function fn2(){
console.log(2)
}
fn1()//1
fn2()//2
- 异步任务指被引擎挂起的、不在主线程上排队的任务,只有在满足某种条件后,引擎认为可以执行了,异步任务才会进入主线程,如:
function fn1(){
setTimeout(function(){
console.log(1)
},1000)
}
function fn2(){
console.log(2)
}
fn1()
fn2()//先打出2,再打出1
回调函数(callback)是异步操作最基本的方法,代表着当异步操作完成就调用被传入的函数。基本格式如下:
function fn1(callback){
callback()
}//fn1是一个异步操作
function fn2(){
console.log(2)
}
fn1(fn2)
二、什么是回调地狱
- 回调函数容易理解和实现,但存在一个问题:回调地狱(callback hell)。
- 回调地狱是指,当多个回调函数嵌套时,容易使得代码程序结构混乱、不容易被读懂,代码非常难看。如:
function fn1(){
}
fn1(function (){
fn2(function (){
fn3(function(){
fn4()
})
})
})
- 解决回调地狱的方法:
1、给函数命名,减少函数嵌套,如:
function fn1(){
}
function fn2(){
fn3()
}
function fn3(){
fn4()
}
function fn4(){
}
fn1(fn2)
2、模块化
这里不展开讲。
3、用Promise
三、Promise
详细用法可参考阮一峰ES6的笔记
所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。
Promise对象有以下两个特点:
(1)对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。
(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
基本的用法如下:
声明一个函数,返回一个Promise
对象,成功就调用resolve()
,失败则调用reject()
;
执行这个函数时,成功就调用.then(arg1,arg2)
里的第一个函数arg1
,失败则调用第二个函数arg2
。
function 获取用户信息(name){
return new Promise(function(resolve,reject){
if(name === '甲'){
console.log('原来是小甲')
resolve('甲')
}else{
console.log('谁啊不认识')
reject('不认识')
}
})
}
function 打印信息(data){
return new Promise(function(resolve,reject){
console.log(data)
resolve(data)
})
}
function 获取好友信息(name){
return new Promise(function(resolve,reject){
if(name === '甲'){
resolve('甲的好友有:张三、李四')
}else{
reject('不知道')
}
})
}
function 获取失败理由(reason){
console.log('失败的理由是:'+reason)
return Promise.reject('不认识')
}
function 修正前面的错误(){
console.log('我搞不定了,求大神来帮忙')
}
获取用户信息('乙')
.then(打印信息,获取失败理由)
.then(获取好友信息,修正前面的错误)
.then(打印信息)
.catch(console.log('那就这样吧'))
四、async和await
要获取异步函数的结果时,可以采用await
,如:
function 获取用户信息(name){
return new Promise(function(resolve,reject){
if(name === '甲'){
console.log('原来是小甲')
resolve('甲')
}else{
console.log('谁啊不认识')
reject('不认识')
}
})
}
function 打印信息(data){
return new Promise(function(resolve,reject){
console.log(data)
resolve(data)
})
}
function 获取好友信息(name){
return new Promise(function(resolve,reject){
if(name === '甲'){
resolve('甲的好友有:张三、李四')
}else{
reject('不知道')
}
})
}
function 获取失败理由(reason){
console.log('失败的理由是: '+reason)
return Promise.reject('不认识')
}
function 修正前面的错误(){
console.log('我搞不定了,求大神来帮忙')
}
try{
let 用户信息 = await 获取用户信息('乙')
console.log(用户信息)
}catch(error){
console.log('失败的理由是: '+error)
}
因为这是用同步的形式来写异步函数,浏览器无法识别是同步还是异步,所以在函数内部使用await
时,在声明函数时要在声明前写async
,否则会报错。
function buyFruit(){
setTimeout(function(){
console.log('apple')
},5000)
}
function fn(){
var result = await buyFruit()
console.log(result)
}
fn()//Uncaught SyntaxError: await is only valid in async function
正确的写法是:
function buyFruit(){
setTimeout(function(){
console.log('apple')
},5000)
}
async function fn(){
var result = await buyFruit()
console.log(result)
}
fn()