是一个保存了异步事件将来执行结果的对象。它不是一个新技术,而是一种能够优化异步编程编码风格的规范。最先诞生于社区,用于解决JavaScript代码中回调嵌套层级过多的问题和错误处理逻辑堆砌的问题。使用Promise对象封装异步操做,可让代码变得直观、线性、优雅。javascript
用Promise封装Ajax请求java
//先写一个原始的Ajax请求 let xhr = new XMLHttpRequest() function resolve(v){console.log(v);} function reject(e){console.log(e);} xhr.onerror = function(e){ reject(e) } xhr.ontimeout = function(e){ reject(e) } xhr.onreadystatechange = function(){ if(xhr.readyState===4){ if(xhr.status===200){ resolve(xhr.response) } } } xhr.open('Get','https://wwww.google.com',true) xhr.send() // 初版:利用Promise封装ajax let p = new Promise((resolve,reject)=>{ let xhr = new XMLHttpRequest() xhr.onerror = function(e){ reject(e) } xhr.ontimeout = function(e){ reject(e) } xhr.onreadystatechange = function(){ if(xhr.readyState===4){ if(xhr.status===200){ resolve(xhr.response) } } } xhr.open('Get','https://wwww.google.com',true) xhr.send() }); p.then(v=>{ console.log("我是成功时注册的回调"); },e=>{ console.log("我是失败时注册的回调"); }) // 第二版 支持传参、封装了Promise建立的细节 function Xpromise(request){ function executor(request,resolve,reject){ let xhr = new XMLHttpRequest() xhr.onerror = function(e){ reject(e) } xhr.ontimeout = function(e){ reject(e) } xhr.onreadystatechange = function(){ if(xhr.readyState===4){ if(xhr.status===200){ resolve(xhr.response) } } } xhr.open('Get',request.url,true) xhr.send() } renturn new Promise(executor); } let x1 = Xpromise(makeRequest('https://wwww.google.com')).then(v=>{ console.log("我是成功时注册的回调"); },e=>{ console.log("我是失败时注册的回调"); })
另外,Promise还提供了一系列好用的API,如静态resolve()、all()、race()方法等。node
Promise用回调函数延迟绑定、回调函数onResolve返回值穿透机制解决回调嵌套层级过多的问题;使用错误冒泡机制简化了错误处理逻辑堆砌的问题。ajax
// 第一点:Promise是一个类 class MyPromise { // 第二点:Promised构造函数的参数是一个函数; constructor(fn) { if (typeof fn !== "function") { throw new Error("promise的构造函数参数应该为函数类型") } // 第三点:Promise的内部状态有三个,Promise对象具备值 this._status = PENDING; this._value = undefined; // 第五点:new Promise(fn)时,就须要执行fn作业务逻辑,故构造函数里就要调用fn函数。此处内部函数_resolve和_reject会被调用用于追踪Promise的内部状态 try { //注意用try-catch包住 fn(this._resolve.bind(this), this._reject.bind(this)); } catch (err) { this._reject(err) } } // 第四点:定义MyPromise状态翻转时,要执行的内部函数 _resolve(val){ if (this._status !== this.PENDING) return //这段代码体现了Promise的状态翻转:只能是P->F或者是P->R this._status = FULLFILLED; this._value = val; }; _reject(err){ if (this._status !== this.PENDING) return this._status = REJECTED; this._value = err; }; }
class MyPromise { constructor(fn) { if (typeof fn !== "function") { throw new Error("myPromise的构造函数参数应该为函数类型") } this._status = PENDING; this._value = undefined; //特性2-新增定义两个回调函数数组 this.fulfilledQueue = []; this.rejectedQueue = []; try { fn(this._resolve.bind(this), this._reject.bind(this)); } catch (err) { this._reject(err) } } _resolve(val){ if (this._status !== this.PENDING) return // 特性4:注册的回调函数在Promise状态翻转时会执行,执行的方式是循环从队列里面取出回调执行 // 定义run函数 run = (value)=>{ this._status = FULLFILLED; this._value = val; let ck; while(ck = this.fulfilledQueue.shift()){ ck(value); } } // 特性5:run()函数的执行,这里很是关键。要把run放进setTimeOut。为何? // 由于执行_resolve()函数时,then()可能还没执行,因此为了让then()中的回调、包括链式调用的then()的回调添加到fulfilledQueue中, // 须要延迟执行run()。实际上这里用setTimeout性能差,实际中采用微任务的方式实现 setTimeout(run,0) //run(); }; _reject(err){ if (this._status !== this.PENDING) return run = (error)=>{ this._status = this.REJECTED; this._value = err; let ck; while(ck = this.rejectedQueue.shift()){ ck(error) } } setTimeout(run,0); }; // 最重要的then函数: // 特性1-用于注册回调,then()函数有两个参数,都是可选的,若是参数不是函数将会被忽略 // 同一个Promise能够屡次注册then(),特性2-因此Promise内部要维护两个数组,分别存储then上注册的成功回调和失败回调); // then()支持链式调用,特性3-之因此支持是由于其返回值是一个新的Promise,此处要完成回调函数onFulfilled穿透机制的实现、错误冒泡机制的实现 //特性1-回调函数的注册:故为它传递两个回调函数占位 then(onFulfilled,onRejected){ const {_status, _value} = this; //特性3-返回一个新的promise return new MyPromise((onFulfilledNext, onRejectedNext)=>{ let fulfilled = value => { try{ if(typeof onFulfilled != "function"){ //若是 onFulfilled 或 onRejected 不是函数,onFulfilled 或 onRejected 被忽略 onFulfilledNext(value) }else{ //若是 onFulfilled 或者 onRejected 返回一个值 res let res = onFulfilled(value) if(res instanceof MyPromise){ //若 res 为 Promise ,这时后一个回调函数,就会等待该 Promise 对象(即res )的状态发生变化,才会被调用,而且新的 Promise 状态和 res 的状态相同 res.then(onFulfilledNext, onRejectedNext) }else{ //若 res 不为 Promise ,则使res 直接做为新返回的 Promise 对象的值 onFulfilledNext(res) } } }catch(err){ onRejectedNext(err) } } let rejected = error => { try{ if(typeof onRejected != "function"){ onRejectedNext(error) }else{ let res = onRejectedNext(error) if(res instanceof MyPromise){ res.then(onFulfilledNext, onRejectedNext) }else{ onFulfilledNext(res); } } }catch(err){ onRejectedNext(err) } } switch(_status){ case PENDING: //在 promise 状态改变前, onFulfilled或者onRejected不可被调用 this._fulfilledQueue.push(onFulfilled) this._rejectedQueue.push(onRejected) break case FULFILLED: //onFulfilled调用次数只有一次,fulfilled对onFulFilled进行包装。why need 包装?由于onFulFilled多是函数、可能不是,若是是函数,返回值多是Promise可能不是 fulfilled(_value) break case REJECTED: //onRejected调用次数只有一次 rejected(_value) break } }) } }
class MyPromise { constructor(handle) { if (!isFunction(handle)) { throw new Error('MyPromise must accept a function as a parameter') } this._status = PENDING this._value = undefined this._fulfilledQueues = [] this._rejectedQueues = [] try { handle(this._resolve.bind(this), this._reject.bind(this)) } catch (err) { this._reject(err) } } // 添加resovle时执行的函数 _resolve(val) { const run = () => { if (this._status !== PENDING) return const runFulfilled = (value) => { let cb; while (cb = this._fulfilledQueues.shift()) { cb(value) } } const runRejected = (error) => { let cb; while (cb = this._rejectedQueues.shift()) { cb(error) } } /* 若是resolve的参数为Promise对象,则必须等待该Promise对象状态改变后, 当前Promsie的状态才会改变,且状态取决于参数Promsie对象的状态, 所以这里进行逻辑区分 */ if (val instanceof MyPromise) { val.then(value => { this._value = value this._status = FULFILLED runFulfilled(value) }, err => { this._value = err this._status = REJECTED runRejected(err) }) } else { this._value = val this._status = FULFILLED runFulfilled(val) } } setTimeout(run, 0) } _reject(err) { if (this._status !== PENDING) return const run = () => { this._status = REJECTED this._value = err let cb; while (cb = this._rejectedQueues.shift()) { cb(err) } } setTimeout(run, 0) } then(onFulfilled, onRejected) { const { _value, _status } = this return new MyPromise((onFulfilledNext, onRejectedNext) => { let fulfilled = value => { try { if (!isFunction(onFulfilled)) { onFulfilledNext(value) } else { let res = onFulfilled(value); if (res instanceof MyPromise) { res.then(onFulfilledNext, onRejectedNext) } else { onFulfilledNext(res) } } } catch (err) { onRejectedNext(err) } } let rejected = error => { try { if (!isFunction(onRejected)) { onRejectedNext(error) } else { let res = onRejected(error); if (res instanceof MyPromise) { res.then(onFulfilledNext, onRejectedNext) } else { onFulfilledNext(res) } } } catch (err) { onRejectedNext(err) } } switch (_status) { case PENDING: this._fulfilledQueues.push(fulfilled) this._rejectedQueues.push(rejected) break case FULFILLED: fulfilled(_value) break case REJECTED: rejected(_value) break } }) } }
class MyPromise { constructor(handle) { if (!isFunction(handle)) { throw new Error('MyPromise must accept a function as a parameter') } this._status = PENDING this._value = undefined this._fulfilledQueues = [] this._rejectedQueues = [] try { handle(this._resolve.bind(this), this._reject.bind(this)) } catch (err) { this._reject(err) } } _resolve(val) { const run = () => { if (this._status !== PENDING) return const runFulfilled = (value) => { let cb; while (cb = this._fulfilledQueues.shift()) { cb(value) } } const runRejected = (error) => { let cb; while (cb = this._rejectedQueues.shift()) { cb(error) } } if (val instanceof MyPromise) { val.then(value => { this._value = value this._status = FULFILLED runFulfilled(value) }, err => { this._value = err this._status = REJECTED runRejected(err) }) } else { this._value = val this._status = FULFILLED runFulfilled(val) } } setTimeout(run, 0) } _reject(err) { if (this._status !== PENDING) return const run = () => { this._status = REJECTED this._value = err let cb; while (cb = this._rejectedQueues.shift()) { cb(err) } } setTimeout(run, 0) } then(onFulfilled, onRejected) { const { _value, _status } = this return new MyPromise((onFulfilledNext, onRejectedNext) => { let fulfilled = value => { try { if (!isFunction(onFulfilled)) { onFulfilledNext(value) } else { let res = onFulfilled(value); if (res instanceof MyPromise) { res.then(onFulfilledNext, onRejectedNext) } else { onFulfilledNext(res) } } } catch (err) { onRejectedNext(err) } } let rejected = error => { try { if (!isFunction(onRejected)) { onRejectedNext(error) } else { let res = onRejected(error); if (res instanceof MyPromise) { res.then(onFulfilledNext, onRejectedNext) } else { onFulfilledNext(res) } } } catch (err) { onRejectedNext(err) } } switch (_status) { case PENDING: this._fulfilledQueues.push(fulfilled) this._rejectedQueues.push(rejected) break case FULFILLED: fulfilled(_value) break case REJECTED: rejected(_value) break } }) } // 添加catch方法 catch(onRejected) { return this.then(undefined, onRejected) } // 添加静态resolve方法 static resolve(value) { // 若是参数是MyPromise实例,直接返回这个实例 if (value instanceof MyPromise) return value return new MyPromise(resolve => resolve(value)) } // 添加静态reject方法 static reject(value) { return new MyPromise((resolve, reject) => reject(value)) } // 添加静态all方法 static all(list) { return new MyPromise((resolve, reject) => { /** * 返回值的集合 */ let values = [] let count = 0 for (let [i, p] of list.entries()) { // 数组参数若是不是MyPromise实例,先调用MyPromise.resolve this.resolve(p).then(res => { values[i] = res count++ // 全部状态都变成fulfilled时返回的MyPromise状态就变成fulfilled if (count === list.length) resolve(values) }, err => { // 有一个被rejected时返回的MyPromise状态就变成rejected reject(err) }) } }) } // 添加静态race方法 static race(list) { return new MyPromise((resolve, reject) => { for (let p of list) { // 只要有一个实例率先改变状态,新的MyPromise的状态就跟着改变 this.resolve(p).then(res => { resolve(res) }, err => { reject(err) }) } }) } finally(cb) { return this.then( value => MyPromise.resolve(cb()).then(() => value), reason => MyPromise.resolve(cb()).then(() => { throw reason }) ); } }
Promise是微任务的一种实现,给出以下的代码,分析其输出顺序编程
//题目1 async function a1 () { console.log('a1 start') await a2() console.log('a1 end') } async function a2 () { console.log('a2') } console.log('script start') setTimeout(() => { console.log('setTimeout') }, 0) Promise.resolve().then(() => { console.log('promise1') }) a1() let promise2 = new Promise((resolve) => { resolve('promise2.then') console.log('promise2') }) promise2.then((res) => { console.log(res) Promise.resolve().then(() => { console.log('promise3') }) }) console.log('script end') //题目2 async function async1() { console.log('async1 start'); await async2(); await async3() console.log('async1 end'); } async function async2() { console.log('async2'); } async function async3() { console.log('async3'); } console.log('script start'); setTimeout(function() { console.log('setTimeout'); }, 0) async1(); new Promise(function(resolve) { console.log('promise1'); resolve(); }).then(function() { console.log('promise2'); }); console.log('script end'); //题目3 async function async1(){ console.log('async1 start') await async2() console.log('async1 end') } async function async2(){ console.log('async2') } console.log('script start') setTimeout(function(){ console.log('setTimeout0') },0) setTimeout(function(){ console.log('setTimeout3') },3) setImmediate(() => console.log('setImmediate')); process.nextTick(() => console.log('nextTick')); async1(); new Promise(function(resolve){ console.log('promise1') resolve(); console.log('promise2') }).then(function(){ console.log('promise3') }) console.log('script end')
答案:
题目一:script start->a1 start->a2->promise2->script end->promise1->a1 end->promise2.then->promise3->setTimeout
题目二:script start->async1 start->async2->promise1->script end->async3->promise2->async1 end->setTimeout
在浏览器console可实验
题目三:script start->async1 start->async2->promise1->promise2
->script end->nextTick->async1 end->promise3->setTimeout->setImmediate->setTimeout3
在node环境中可实验api
all()的实现:函数中维护了一个数组和计数器,数组的大小为初始时all函数中传递的Promise对象数量,数组存储各个Promise执行成功后resolve获得的结果,每成功一个计数器+1,直到计数器累加到数组大小时即调用resolve(value),只要有一个Promise执行到失败的回调,即所有失败。
链式调用的实现:then函数返回的依然是一个Promise,见第二版的Promise实现。数组
方法1:使用allSettled替代;方法2:改写Promise,将reject操做换成是resolve(new Error("自定义的错误"));方法3:引入第三方库promise-transactionpromise
问题1:没有提供中途取消的机制;问题2:必需要设置回调,不然内部错误没法在外部反映出来;问题3:使用时依然存在大量Promise的api,逻辑不清晰。浏览器