Promise
Promise的基本用法
- Promise是一个构造函数,必须接受一个回调函数作为参数(resolve,reject作为参数)js
let p = new Promise((resolve, reject) => { //dosomething 同步调用的 resolve(n) //n传给then //dosomething reject(n) //两者只会执行一个,一旦执行其中一个 //promise的状态就会从pending转变成fulfilled或者rejected,并且无法改变 }) - Promise在then方法中执行其对应的成功和失败回调js
p.then((value)=>{ //value成功值 //成功回调 },(reason)=>{ //reason理由 //失败回调 })
使用promise封装原生sendAjax
- js
function sendAJAX(url){ return new Promise((resolve,reject)=>{ const xhr = new XMLHttpRequest() xhr.open('GET',url) xhr.send() //处理结果 xhr.onreadystatechange = function(){ if(xhr.readystate === 4){ //判断 if(xhr.status >= 200 && xhr.status<=300){ resolve(xhr.response) }else{ reject(xhr.status) } } } }) }
Promise的静态方法
Promise.resolve()
- 有时需要将现有对象转成Promise 对象js
Promise.resolve('foo') // 等价于 new Promise(resolve => resolve('foo')) - 参数情况
- 如果参数是 Promise 实例,那么
Promise.resolve将不做任何修改、原封不动地返回这个实例 - 参数是一个
thenable对象,Promise.resolve方法会将这个对象转为Promise对象,然后就立即执行thenable对象的then方法,jslet thenable = { then: function(resolve, reject) { resolve(42); } }; let p1 = Promise.resolve(thenable); p1.then(function(value) { console.log(value); // 42 //上面代码中,thenable对象的then方法执行后,对象p1的状态就变为resolved,从而立即执行最后那个then方法指定的回调函数,输出 42 }); - 参数不是具有then方法的对象,或根本就不是对象,则
Promise.resolve方法返回一个新的Promise对象,状态为resolved - 不带有任何参数,直接返回一个
resolved状态的Promise对象
- 如果参数是 Promise 实例,那么
Promise.reject(reason)jsPromise.reject() //无论传入什么,返回的都是rejected状态的promise对象 const p = Promise.reject('出错了'); // 等同于 const p = new Promise((resolve, reject) => reject('出错了')) p.then(null, function (s) { console.log(s) }); // 出错了Promise.all()
Promise.all()方法用于将多个Promise实例,包装成一个新的Promise实例jsconst p = Promise.all([p1, p2, p3]); //1.Promise.all()方法接受一个数组作为参数,p1、p2、p3都是 Promise 实例,如果不是,就会先调用下面讲到的Promise.resolve方法,将参数转为 Promise 实例,再进一步处理 //2.Promise.all()方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例 //3.p的状态由p1、p2、p3决定,分成两种情况 //(1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数 //(2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数- 注意,如果作为参数的
Promise实例,自己定义了catch方法,那么它一旦被rejected,并不会触发Promise.all()的catch方法。,因为自己有catch方法,他首先会rejected,然后被自己的catch捕获,然后状态变成了resolved,这样就不会执行.all的catch方法,而是会执行then方法jsconst p1 = new Promise((resolve, reject) => { resolve('hello'); }) .then(result => result) .catch(e => e); const p2 = new Promise((resolve, reject) => { throw new Error('报错了'); }) .then(result => result) .catch(e => e); Promise.all([p1, p2]) .then(result => console.log(result)) .catch(e => console.log(e)); // ["hello", Error: 报错了]
Promise.race()
Promise.race()方法同样是将多个Promise实例,包装成一个新的Promise实例js//传入对象也是一个数组,谁先处理完,返回的状态就以谁为准 const p = Promise.race([ fetch('/resource-that-may-take-a-while'), new Promise(function (resolve, reject) { setTimeout(() => reject(new Error('request timeout')), 5000) }) ]); p .then(console.log) .catch(console.error); //如果指定时间内没有获得结果,就将 Promise 的状态变为reject,否则变为resolve
Promise.allSettled()
Promise.allSettled()方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例。只有等到所有这些参数实例都返回结果,不管是fulfilled还是rejected,包装实例才会结束
Promise.any()
Promise.any()方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例。只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfilled状态;如果所有参数实例都变成rejected状态,包装实例就会变成rejected状态
Promise的实例方法
promise.prototype.then
then方法的第一个参数是resolved状态的回调函数,第二个参数(可选)是rejected状态的回调函数then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法.- 链式调用时,如果第一个then中回调函数返回了一个promise对象,那么后一个then就会等待其状态变化来决定执行对应回调函数js
let p = new Promise((resolve,reject)=>resolve('OK')) let result = p.then(value=>{ //then的返回结果由传入的回调函数的返回值决定 //1.抛出错误 //throw '出了问题' //2. 返回结果是非Promise类型的对象 //return 521 //3.返回结果是Promise对象 //return new Promise((resolve,reject)=>{ //resolve('success') //}) },reason=>{ console.warn(reason) })
promise.prototype.catch- 可以理解成是
.then(null, rejection)或.then(undefined, rejection)的别名 - 建议总是使用
catch()方法,而不使用then()方法的第二个参数 catch可以捕获在其之前抛出的错误,或者是状态为rejected的promise对象
- 可以理解成是
Promise.prototype.finally()finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作finally方法的回调函数不接受任何参数,这意味着没有办法知道,前面的Promise状态到底是fulfilled还是rejected- 本质上就是一个then的特例js
promise.finally(() => { // 语句 }); // 等同于 promise.then( result => { // 语句 return result; }, error => { // 语句 throw error; } );
手写Promise
实现思路:Promise源码实现
模拟实现
js// 模拟实现Promise // Promise利用三大手段解决回调地狱: // 1. 回调函数延迟绑定 // 2. 返回值穿透 // 3. 错误冒泡 // 定义三种状态 const PENDING = 'PENDING'; // 进行中 const FULFILLED = 'FULFILLED'; // 已成功 const REJECTED = 'REJECTED'; // 已失败 class Promise { constructor(exector) { // 初始化状态 this.status = PENDING; // 将成功、失败结果放在this上,便于then、catch访问 this.value = undefined; this.reason = undefined; // 成功态回调函数队列 this.onFulfilledCallbacks = []; // 失败态回调函数队列 this.onRejectedCallbacks = []; const resolve = value => { // 只有进行中状态才能更改状态 保证状态只更改一次 if (this.status === PENDING) { //1.修改对象的状态 this.status = FULFILLED; //2.修改对象结果值 this.value = value; // 成功态函数依次执行 this.onFulfilledCallbacks.forEach(fn => fn(this.value)); } } const reject = reason => { // 只有进行中状态才能更改状态 if (this.status === PENDING) { this.status = REJECTED; this.reason = reason; // 失败态函数依次执行 this.onRejectedCallbacks.forEach(fn => fn(this.reason)) } } try { // 立即执行executor // 把内部的resolve和reject传入executor,用户可调用resolve和reject exector(resolve, reject); } catch(e) { // 对于抛出错误的情况,executor执行出错,将错误内容reject抛出去 reject(e); } } then(onFulfilled, onRejected) { onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value; onRejected = typeof onRejected === 'function'? onRejected : reason => { throw new Error(reason instanceof Error ? reason.message : reason) } // 保存this const self = this; return new Promise((resolve, reject) => { if (self.status === PENDING) { self.onFulfilledCallbacks.push(() => { // try捕获错误 try { // 模拟微任务 setTimeout(() => { const result = onFulfilled(self.value); // 分两种情况: // 1. 回调函数返回值是Promise,执行then操作 // 2. 如果不是Promise,调用新Promise的resolve函数 result instanceof Promise ? result.then(resolve, reject) : resolve(result); }) } catch(e) { reject(e); } }); self.onRejectedCallbacks.push(() => { // 以下同理 try { setTimeout(() => { const result = onRejected(self.reason); // 不同点:此时是reject result instanceof Promise ? result.then(resolve, reject) : resolve(result); }) } catch(e) { reject(e); } }) } else if (self.status === FULFILLED) { try { setTimeout(() => { const result = onFulfilled(self.value); result instanceof Promise ? result.then(resolve, reject) : resolve(result); }); } catch(e) { reject(e); } } else if (self.status === REJECTED) { try { setTimeout(() => { const result = onRejected(self.reason); result instanceof Promise ? result.then(resolve, reject) : resolve(result); }) } catch(e) { reject(e); } } }); } catch(onRejected) { return this.then(null, onRejected); } static resolve(value) { if (value instanceof Promise) { // 如果是Promise实例,直接返回 return value; } else { // 如果不是Promise实例,返回一个新的Promise对象,状态为FULFILLED return new Promise((resolve, reject) => resolve(value)); } } static reject(reason) { return new Promise((resolve, reject) => { reject(reason); }) } static all(promiseArr) { const len = promiseArr.length; const values = new Array(len); // 记录已经成功执行的promise个数 let count = 0; return new Promise((resolve, reject) => { for (let i = 0; i < len; i++) { // Promise.resolve()处理,确保每一个都是promise实例 Promise.resolve(promiseArr[i]).then( val => { values[i] = val; //不用push 防止成功的顺序不一致 count++; // 如果全部执行完,返回promise的状态就可以改变了 if (count === len) resolve(values); }, err => reject(err), ); } }) } static race(promiseArr) { return new Promise((resolve, reject) => { promiseArr.forEach(p => { Promise.resolve(p).then( val => resolve(val), err => reject(err), ) }) }) } }Promise.all
Promise.all是支持链式调用的,本质上就是返回了一个Promise实例,通过resolve和reject来改变实例状态。jsPromise.myAll = function(promiseArr) { return new Promise((resolve, reject) => { const ans = []; let index = 0; for (let i = 0; i < promiseArr.length; i++) { promiseArr[i] .then(res => { ans[i] = res; index++; if (index === promiseArr.length) { resolve(ans); } }) .catch(err => reject(err)); } }) }
Promise.race
jsPromise.race = function(promiseArr) { return new Promise((resolve, reject) => { promiseArr.forEach(p => { // 如果不是Promise实例需要转化为Promise实例 Promise.resolve(p).then( val => resolve(val), err => reject(err), ) }) }) }Promise并行限制
- 就是实现有并行限制的Promise调度器问题。js
class Scheduler { constructor() { this.queue = []; this.maxCount = 2; this.runCounts = 0; } add(promiseCreator) { this.queue.push(promiseCreator); } taskStart() { for (let i = 0; i < this.maxCount; i++) { this.request(); } } request() { if (!this.queue || !this.queue.length || this.runCounts >= this.maxCount) { return; } this.runCounts++; this.queue.shift()().then(() => { this.runCounts--; this.request(); }); } } const timeout = time => new Promise(resolve => { setTimeout(resolve, time); }) const scheduler = new Scheduler(); const addTask = (time,order) => { scheduler.add(() => timeout(time).then(()=>console.log(order))) } addTask(1000, '1'); addTask(500, '2'); addTask(300, '3'); addTask(400, '4'); scheduler.taskStart() // 2 // 3 // 1 // 4
- 就是实现有并行限制的Promise调度器问题。