最近一下班就没有持续学习,已经持续了几个月,回家就是练练字,看看书,干些七七八八的事。这样的生活总感觉空落落的,毕业半年了,总感觉自己很多东西明白的不清不楚,希望接下来的每一天都能扎扎实实学好。
一、出现前景,Promise是什么
- 出现前景:在一个单线程的世界,为了支持异步编程,于是Js使用了回调,然而回调带来的问题就是我们经常听到的什么地狱回调,金字塔等问题。有问题就会有解决方案,所以就出现了Promise。
- 概念:异步编程的一个解决方案
二、基本使用规则
ES6 规定,Promise对象是一个构造函数,用来生成Promise实例
const promise = new Promise(function(resolve, reject) { if (操作成功){ resolve(value); } else { reject(error); } });
从上面代码可以看到Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject,此外这两个参数是连两个函数,由JS引擎提供。
那么这两个函数的作用是:
(1) resolve: 将Promise对象的状态从未完成变成成功,异步操作成功时调用。
(2) reject: 将Promise对象的状态从未完成变成失败,异步操作失败时调用。
当Promise实例生成后,就可以用then方法指定resolve状态和reject状态的回调函数,then方法接受两个回调函数作为参数:
第一个回调是当Promise对象的状态为resolve时调用
第二个回调函数是当Promise对象的状态为reject时调用,第二个回调函数可选
基本案例:
var promise = new Promise(function(resolve, reject) { resolve() console.log('我是当前脚本的同步任务,then方法指定的回调函数等下我,你的在我后面排队') }) promise.then(function(resolve) { console.log('promise的状态为resolve,所以我被调用出来啦') },function(reject) { console.log('promise的状态为reject,我才会出来哦,这次我就不参与') }) 结果输出: 我是当前脚本的同步任务,then方法指定的回调函数等下我,你的在我后面排队 promise的状态为resolve,所以我被调用出来啦
Promise.prototype.then()
Promise 实例具有then方法, 那么then方法是定义在原型对象promise.prototype上的
then方法返回的也是一个新的Promise实例,因此可以采用链式写法,即then方法后面再调用另一个then方法
run1.then(function(data) { console.log(data + ':第一个输出') return run2(); }).then(function(data) { console.log(data + ':第二个输出') return run3() }).then(function(data) { console.log(data + ':第三个输出') })
上面的代码使用then
方法,第一个回调函数完成以后,会将返回结果作为参数,传入第二个回调函数。
Promise.prototype.catch():
用于指定发生错误时的回调函数
run1.then(function(data) { return run2(); }).catch(function(data) { console.log('前方有异常') })
看到这里的时候,可能会觉得promise对象不是有一种失败调用的reject函数么,如下格式:
run1.then(function(data) { return run2(); }, function(data) { console.log('异常调用') })
对比这两种写法,第一种写法要好于第二种写法,理由是通过catch可以捕获前面then
方法执行中的错误,更接近同步的写法(try/catch
)。
因此,建议总是使用catch
方法,而不使用then
方法的第二个参数。
下面是一个用Promise
对象实现的 Ajax 操作的例子:
function getData(url) { let promise = new Promise(function(resolve, reject) { const handler = function() { if (this.readyState !== 4) { return } if (this.status === 200) { resolve(this.response) } else { reject(new Error(this.responseText)) } } let temp = new XMLHttpRequest() temp.open('get', url) temp.onreadystatechange = handler; temp.setRequestHeader("Accept", "application/json"); temp.send() }) return promise } getData('http://yapi.demo.qunar.com/mock/37644/testDat').then(function(data){ console.log(data) return '12' }).then(function(data) { console.log(data) })
三、手写一个promise
* 同步操作,且then只能调用一次
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1.0"> <title>同步操作,且then只能调用一次</title> </head> <body> <div id="box">12</div> <script>function Promise(exector) { // 创建一个构造构造函数 let self = this; this.value = undefined; this.reason = undefined; this.status = 'pending'; function resolve(value) { // 此function this指向window if (self.status === 'pending') { self.value = value; self.status = 'resolved'; } } function reject(reason) { if (self.status === 'pending') { self.reason = reason; self.status = 'rejected'; } } try { exector(resolve, reject) } catch(e) { reject(e) } } Promise.prototype.then = function(onFulfilled, onRejected) { // then方法添加到构造函数的原型上 let self = this; if (this.status === 'resolved') { onFulfilled(self.value) } if (this.status === 'rejected') { onRejected(self.reason) } } let promise1 = new Promise(function(a, b) { a('ha') }) promise1.then(data => { console.log('success') console.log(data); }, err=> { console.log('error') console.log(err); }) </script> </body> </html>
显然我们平时用的都是异步的方式,再来改改。需要在构造函数中存放两个数组,分别保存成功回调和失败的回调 因为可以then多次,所以需要将这些函数放在数组中
* 异步操作,且then只能调用一次
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1.0"> <title>异步操作,且then只能调用一次</title> </head> <body> <div id="box">12</div> <script> function Promise(exector) { // 创建一个构造构造函数 let self = this; this.value = undefined; this.reason = undefined; this.status = 'pending'; this.successCallBack = []; this.failCallBack = []; function resolve(value) { // 此function this指向window if (self.status === 'pending') { self.value = value; self.status = 'resolved'; console.log(self.successCallBack) self.successCallBack.forEach((fn) => { fn() }) // 遍历then中成功的所有回调函数 } } function reject(reason) { if (self.status === 'pending') { self.reason = reason; self.status = 'rejected'; self.failCallBack.forEach((fn) => { fn() }) // 遍历then中失败的所有回调函数 } } try { exector(resolve, reject) } catch(e) { reject(e) } } Promise.prototype.then = function(onFulfilled, onRejected) { // then方法添加到构造函数的原型上 let self = this; if (this.status === 'resolved') { onFulfilled(self.value) } if (this.status === 'rejected') { onRejected(self.reason) } if (this.status === 'pending') { this.successCallBack.push(() => { onFulfilled(self.value) }) this.failCallBack.push(() => { onRejected(self.reason) }) } } let promise1 = new Promise((resolve, reject) => { setTimeout(() => { if(Math.random() > 0.5) { resolve('成功'); } else { reject('失败'); } }, 1000) }) promise1.then(data => { console.log('success') console.log(data); }, err=> { console.log('error') console.log(err); }) </script> </body> </html>
* 异步操作,then可以链式调用