前置知识
一直以来,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状态变为 fulfilledthen() 可以拿到异步数据同理,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 的前提仍是基于 Promiseconst 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))
