前置知识
一直以来,JavaScript处理异步都是以callback的方式,在前端开发领域callback机制几乎深入人心,近几年随着JavaScript开发模式的逐渐成熟,CommonJS规范顺势而生,其中就包括提出了Promise规范,Promise完全改变了js异步编程的写法,让异步编程变得十分的易于理解,同时Promise也已经纳入了ES6,而且高版本的chrome、firefox浏览器都已经原生实现了Promise,只不过和现如今流行的类Promise类库相比少些API
回调地狱callback
doSomething(function(result) {
doSomethingElse(result, function(newResult) {
doThirdThing(newResult, function(finalResult) {
console.log('Got the final result: ' + finalResult);
}, failureCallback);
}, failureCallback);
}, failureCallback);
三个缺陷:
- 命名不规范,有人用success+error,有人用success+fail,有人用done+fail
- 代码可读性低,易出现回调地狱
- 很难捕获错误,进行错误处理
Promise
Promises对象是CommonJS工作组提出的一种规范,目的是为异步编程提供统一接口
前端解决异步问题的统一方案
三个优势:
- 解决回调地狱,增强代码可读性
- 规范回调的名字或顺序
- 很方便地捕获错误
简单说,它的思想是,每一个异步任务返回一个Promise对象,该对象有一个then方法,允许指定回调函数。比如,f1的回调函数f2,可以写成:
f1().then(f2);
Promise
对象:是由关键字 new
及其构造函数来创建的,该对象中传入表示成功和失败的函数,该函数的形参接收成功和失败两个函数
Promise使用
resolve() 可以将异步数据传递出来,并将promise状态变为 fulfilled
then() 可以拿到异步数据
同理,reject()和catch()也是如此
//获取奶茶的方法
function getTea(fn){
setTimeout(()=>{
fn('奶茶')
},500)
}
//获取火锅的方法
function getHotpot(fn){
setTimeout(()=>{
fn('火锅')
},1000)
}
//调用获取火锅再获取奶茶的方法
//默认按时间来讲应该是先获取到奶茶再获取火锅,但按指定顺序获取的话,需要在回调中嵌套回调
getTea(function(data){
console.log(data)
getHotpot(function(data){ //多重嵌套容易出现回调地狱
console.log(data)
})
})
const getTea =()=>{
return new Promise((resolve)=>{
setTimeout(()=>{
resolve('奶茶')
},500)
})
}
const getHotpot =()=>{
return new Promise((resolve)=>{
setTimeout(()=>{
resolve('火锅')
},1000)
})
}
getTea().then(
(data)=>{console.log(data)}
return getHotpot()
).then(
(data)=>{console.log(data)}
)
//async 的前提仍是基于 Promise
const getTea =()=>{
return new Promise((resolve)=>{
setTimeout(()=>{
resolve('奶茶')
},500)
})
}
const getHotpot =()=>{
return new Promise((resolve)=>{
setTimeout(()=>{
resolve('火锅')
},1000)
})
}
//async 只是多封装一层async函数,然后调用这个async函数得到异步任务返回值
async funtion getData(){
let hotPot = await getHotpot() //await 代替 then 拿到resolve传递出来的一部数据
console.log(hotPot)
let tea = await getTea()
console.log(tea)
}
//调用async 函数
getData()
**return new Promose( (resolve, reject) => { } )**
该构造函数接受两个函数——resolve
和 reject
——作为其参数。
当异步任务顺利完成且返回结果值时,会调用 resolve
函数;
而当异步任务失败且返回失败原因(通常是一个错误对象)时,会调用reject
函数。
而resolve函数和reject函数则在.then()形参中声明了,即构成回调
resolve函数和reject函数都只接受一个参数
const myFirstPromise = new Promise((resolve, reject) => {
// ?做一些异步操作,最终会调用下面两者之一:
//
// resolve(someValue); // fulfilled
// ?或
// reject("failure reason"); // rejected
});
以AJAX的封装和调用为例子,来解释promise的用法:
纯AJAX封装和AJAX调用:
AJAX+Promise 示例:
AJAX源码封装上新增Promise对象:
return new Promose( (resolve, reject) => { } )
如何得到ajax()返回的对象?
在ajax封装源码的时候,在ajax函数的开头直接返回Promise构造函数的对象,该对象中传入表示成功和失败的函数,该函数的形参接收成功resolve和失败reject两个函数(固定函数命名),而这两个函数又会各自去调用尔后声明了的then的第一个实参或第二个实参
AJAX调用上采用链式:
调用的时候传“预案”
ajax()返回了一个含有.then()方法的对象,以便进行链式操作
then方法的传入两个参数函数,第一个参数是成功函数,第二个参数是失败函数,即两个回调函数
两个回调函数只能接收一个形参
Axios库
目前最新的AJAX库,抄袭jQuery的封装思路
代码示例:
axios.get('/5.json')
.then(response => console.log(response)
)