ES6中一个很是重要和好用的特性就是Promise类。javascript
可是初次接触Promise会一脸懵逼,这TM是什么东西?看看官方或者一些文章对它的介绍和用法也是一头雾水。java
Promise究竟是作什么的呢? => Promise是异步编程的一种解决方案。通常状况下有异步操做时,使用Promise对这个异步操做进行封装。ajax
那何时咱们会来处理异步事件呢?一种很常见的场景应该就是 网络请求 了。编程
咱们封装一个网络请求的函数不能当即拿到结果,因此不能像简单的 3+4=7 同样将结果返回。因此咱们每每会传入另一个函数,在数据请求成功时将数据经过传入的函数回调出去。若是只是一个简单的网络请求,那么这种方案不会给咱们带来很大的麻烦。可是当网络请求很是复杂时就会出现回调地狱。promise
OK,我以一个很是夸张的案例来讲明。咱们来考虑下面的场景(有夸张的成分):服务器
$.ajax('url1',function(data1) { $.ajax(data1['url2'],function(data2) { $.ajax(data2['url3'],function(data3) { $.ajax(data3['url4'],function(data4) { console.log(data4); }) }) }) })
上面的代码有什么问题吗?正常状况下不会有什么问题,能够正常运行而且获取咱们想要的结果。可是这样额代码难看并且不容易维护,咱们更加指望的是一种更加优雅的方式来进行这种异步操做。如何作呢?就是使用Promise,Promise能够以一种很是优雅的方式来解决这个问题。网络
咱们先来看看Promise最基本的语法。异步
这里咱们用一个定时器来模拟异步事件:假设下面的data是从网络上1秒后请求的数据,console.log就是咱们的处理方式。异步编程
setTimeout(() => { console.log('Hello World'); },1000)
上面是咱们过去的处理方式,咱们将它用Promise进行封装(虽然这个例子会让咱们感受脱裤放屁画蛇添足)函数
new Promise((resolve,reject) => { setTimeout(() => { resolve('Hello World') //reject('Error Data') }, 1000) }).then(data => { console.log(data) //Hello World }).catch(error => { console.log(error) //Error Data }) //注意:另外一种写法,成功和失败的消息均可以写在then这个回调函数中 new Promise((resolve,reject) => { setTimeout(() => { resolve('Hello World') //reject('Error Data') }, 1000) }).then(data => { console.log(data) //Hello World },error => { console.log(error) //Error Data })
咱们先来认认真真的读一读这个程序到底作了什么?
new Promise很明显是建立一个Promise对象
小括号中(resolve, reject) => {}
也很明显就是一个函数,并且咱们这里用的是箭头函数
可是resolve, reject它们是什么呢?
咱们先知道一个事实:在建立Promise时传入的这个箭头函数是固定的(通常咱们都会这样写)
resolve 和 reject 它们两个也是函数,一般状况下咱们会根据请求数据的成功和失败来决定调用哪个。
成功仍是失败?
resolve(messsage)
,这个时候咱们后续的then会被回调。reject(error)
,这个时候咱们后续的catch会被回调。OK,这就是Promise最基本的使用了。
首先, 当咱们开发中有异步操做时, 就能够给异步操做包装一个Promise
异步操做以后会有三种状态
pending
:等待状态,好比正在进行网络请求,或者定时器没有到时间。fulfill
:知足状态,当咱们主动回调了resolve时,就处于该状态而且会回调 then()reject
:拒绝状态,当咱们主动回调了reject时,就处于该状态而且会回调 catch()new Promise((resolve,reject) => { setTimeout(() => { //resolve('Hello World') reject('Error Data') }, 1000) }).then(data => { console.log(data) }).catch(error => { console.log(error) })
new Promise((resolve, reject) => { //1.第一次模拟网络请求的代码 setTimeout(() => { resolve("Hello World"); }, 2000); }).then((data) => { //第一次拿到结果的处理代码 console.log(data); //Hello World return new Promise((resolve, reject) => { //第二次模拟网络请求的代码 setTimeout(() => { resolve(data + " 111"); }, 2000); }).then((data) => { //第二次拿到结果的处理代码 console.log(data); //Hello World 111 return new Promise((resolve, reject) => { //第三次模拟网络请求的代码 setTimeout(() => { resolve(data + "222"); }, 2000); }).then((data) => { //第三次拿到结果的处理代码 console.log(data); //Hello World 111222 return new Promise((resolve, reject) => { //第四次模拟网络请求错误的代码 setTimeout(() => { reject(data + "error"); }, 2000); }).then((data) => { //这里没有输出,这部分代码不会执行 console.log(data); return new Promise((resolve, reject) => { setTimeout(() => { resolve(data + "333"); }, 2000); }); }).catch((data) => { //第四次拿到结果的处理代码 console.log(data); //Hello World 111222error //第五次模拟网络请求的代码 return new Promise((resolve, reject) => { setTimeout(() => { resolve(data + "444"); }, 2000); }).then((data) => { //第五次拿到结果的处理代码 console.log(data); //Hello World 111222error444 //..不能再套娃了 }); }); }); }); }); //注意:其实reject是可选的,当咱们不用的时候能够只写 resolve => {}
只有第一次调用是异步操做,后面的调用不是异步操做可是咱们但愿后面的调用也是分层的
new Promise((resolve,reject) => { setTimeout(() => { resolve('Hello World') }, 1000) }).then(data => { console.log(data) //Hello World return Promise.resolve(data + ' 111') }).then(data => { console.log(data) //Hello World 111 return Promise.resolve(data + '222') }).then(data => { console.log(data) //Hello World 111222 return Promise.reject(data + 'error') }).then(data => { console.log(data) return Promise.resolve(data + '333') }).catch(data => { console.log(data) //Hello World 111222error return Promise.resolve(data + ' 444') }).then(data => { console.log(data) //Hello World 111222error444 })
这里咱们直接经过Promise包装了一下新的数据,将Promise对象返回了
Promise.resovle()
:将数据包装成Promise对象,而且在内部回调resolve()
函数
Promise.reject()
:将数据包装成Promise对象,而且在内部回调reject()
函数
链式调用简写
简化版代码:若是咱们但愿数据直接包装成Promise.resolve,那么在then中能够直接返回数据
注意下面的代码中我将return Promise.resovle(data)
改为了return data
结果依然是同样的
new Promise((resolve,reject) => { setTimeout(() => { resolve('Hello World') }, 1000) }).then(data => { console.log(data) //Hello World return data + ' 111' }).then(data => { console.log(data) //Hello World 111 return data + '222' }).then(data => { console.log(data) //Hello World 111222 return Promise.reject(data + 'error') }).then(data => { console.log(data) return data + '333' }).catch(data => { console.log(data) //Hello World 111222error return data + ' 444' }).then(data => { console.log(data) //Hello World 111222error444 })
假设有两个网络请求,咱们必需要保证两个网络请求都成功后才能执行一些操做。即两个网络请求加上后续的操做才是一个完整的业务。怎么实现呢?
之前的实现方式
//两个flag let isResult1 = false; let isResult2 = false; //第一个请求 $.ajax({ url:'url1' success: () => { console.log("结果一"); isResult1 = true handleResult() } }) //第二个请求 $.ajax({ url:'url2' success: () => { console.log("结果二"); isResult2 = true handleResult() } }) function handleResult() { if(isResult1 && isResult2) { //后续操做 } }
Promise.all([ new Promise((resolve,reject) => { //模拟网络请求一 setTimeout(() => { resolve('result1'); },1000) }), new Promise((resolve,reject) => { //模拟网络请求二 setTimeout(() => { resolve('result2'); },5000) }), ]).then(results => { //5秒后才会打印 console.log(results[0]); //结果一 console.log(results[1]); //结果二 })