1. 这是个什么东西--------console.dir(Promise) 打印出来
Promise是一个构造函数,自己身上有all、allsettled、any、race、reject、resolve这几个方法,原型上有then、catch等方法。因此Promise new出来的对象肯定就有then、catch方法
Promise的构造函数接收一个参数,是函数,并且传入两个参数:resolve,reject,分别表示异步操作执行成功后的回调函数和异步操作执行失败后的回调函数。
3. 用起来是怎么样
function a() { var b = new Promise(function (resolve, reject) { //异步操作 setTimeout(function () { console.log('完成第一次'); resolve('resolve-1') }, 2000); }); return b; } a().then((data)=> { console.log('resolve-1:',data); //后面可以用传过来的数据做些其他操作 //在a()的返回上直接调用then方法,then接收一个参数,是函数,并且会拿到我们在a中调用resolve时传的的参数。运行这段代码,会在2秒后输出“完成”,紧接着输出“输出的东西”。 var c= new Promise(function (resolve, reject) { //异步操作 setTimeout(function () { console.log('完成第二次'); resolve('resolve-2') }, 3000); }); return c; }).then((data)=> { console.log('resolve-2:',data); return '我return一个常量' }).then((data)=> { console.log('resolve-3:',data); }).then((data)=> { console.log(data); });
reject:参数resolve用法相同,需要注意的就是-可以在then方法中第二个参数来接收reject返回值,如果then中没写的话,catch也可以接收到reject的返回值
注意点: 用Promise的时候一般是包在一个函数中,在需要的时候去运行这个函数---再用异步的方法
在then方法中,可以直接return数据或者Promise对象,在后面的then中就可以接收到数据
优势: Promise的优势在于,可以在then方法中继续写Promise对象并返回,然后继续调用then来进行回调操作
3.Promise就是一个容器
有三个状态:pending(进行中)、fulfilled(成功)、rejected(失败),里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果,有两大特点:
1. 容器状态不受外界影响
2. 一旦状态改变就不会再变,任何时候都可以得到这个结果
4. then() 的用法
then方法可以接受两个参数,第一个对应resolve的回调,第二个对应reject的回调。所以我们能够分别拿到他们传过来的数据
5. catch() 的用法---Promise.prototype.catch方法是.then(null, rejection)的别名
和then的第二个参数一样,用来指定reject的回调--------即便是有错误的代码也不会报错,与try/catch语句有相同的功能。
function a() { var b = new Promise(function (resolve, reject) { //异步操作 setTimeout(function () { reject('reject-1') }, 2000); }); return b; } a().then((data)=> { console.log(data); },res=>{ console.log('走的then第二个参数',res) }).catch((data)=> { console.log('走的catch',data) });
6. finally()方法
finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。
finally方法的回调函数不接受任何参数,这意味着没有办法知道,前面的 Promise 状态到底是fulfilled还是rejected。
这表明,finally方法里面的操作,是与状态无关的,不依赖于 Promise 的执行结果。
var promise = new Promise(function(resolve, reject) { console.log("promise") window.setTimeout(function(){ if (false){ resolve('success'); } else { reject('error'); } },1000) }).then(function(){ console.log('success'); }).catch(function(){ console.log('catch'); }).finally(function(){ console.log('finally'); });
应用场景:不需要知道异步操作的状态,去执行一些操作,例如关闭页面提示、弹窗等
7. Promise.resolve()---有时需要将现有对象转为 Promise 对象,Promise.resolve方法就起到这个作用
function resolve(value) { if (value instanceof Promise) { // 如果是Promise实例,直接返回 return value; } else { // 如果不是Promise实例,返回一个新的Promise对象,状态为FULFILLED return new Promise((resolve, reject) => resolve(value)); } }
Promise.resolve方法允许调用时不带参数,直接返回一个resolved状态的 Promise 对象。所以,如果希望得到一个 Promise 对象,比较方便的方法就是直接调用Promise.resolve方法。
const p = Promise.resolve(); //变量p就是一个 Promise 对象 p.then(function () { // ... });
立即resolve的 Promise 对象,是在本轮“事件循环”(event loop)的结束时,而不是在下一轮“事件循环”的开始时
原因:传递到 then()
中的函数被置入了一个微任务队列,而不是立即执行,这意味着它是在 JavaScript 事件队列的所有运行时结束了,事件队列被清空之后,才开始执行
setTimeout(function () { console.log('promise-3'); }, 0); Promise.resolve().then(function () { console.log('promise-2'); }); console.log('promise-1');
resolve()本质作用
resolve()是用来表示promise的状态为fullfilled,相当于只是定义了一个有状态的Promise,但是并没有调用它;
promise调用then的前提是promise的状态为fullfilled;
只有promise调用then的时候,then里面的函数才会被推入微任务中
8.Promise.reject()
function reject(reason) { return new Promise((resolve, reject) => { reject(reason); }) }
类方法,且与resolve唯一的不同是,返回的 promise 对象的状态为 rejected
Promise.reject()方法的参数,会原封不动地作为reject的理由,变成后续方法的参数。这一点与Promise.resolve方法不一致
const thenable = { then(resolve, reject) { reject('出错了'); } }; Promise.reject(thenable) .catch(e => { console.log(e === thenable) }) // true
9.Promise.all()的用法
Promise的all方法提供了并行执行异步操作的能力,并且在所有异步操作执行完后才执行回调
all会把所有异步操作的结果放进一个数组中传给then----可以并行执行多个异步操作,并且在一个回调中处理所有的返回数据
let wake = (time) => { return new Promise((resolve, reject) => { setTimeout(() => { resolve(`${time / 1000}秒后输出`) }, time) }) } let p1 = wake(3000) let p2 = wake(2000) Promise.all([p1, p2]).then((result) => { console.log(result) // [ '3秒后输出', '2秒后输出' ] }).catch((error) => { console.log(error) })
注意:
1. 所有的promise状态变为fulfilled
,返回的promise状态才变为fulfilled
2. 只要有其中一个promise实例是rejected,就会直接走catch方法,并且catch中只会返回第一个变成rejected的promise的错误
3. 数组中的每一项都是一个Promise的对象实例(如果不是,就会先调用Promise.reslove方法,将参数转化为Promise对象实例,再进行下一步的处理)
function 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; count++; // 如果全部执行完,返回promise的状态就可以改变了 if (count === len) resolve(values); }, err => reject(err), ); } }) }
只有当所有入参的promise实例都是fulfilled状态,才会在Promise.all().then()方法中结果,返回结果也是与入参一一对应,结果中只包含实际的resolve的结果,不包含类似allSettled的status和value属性。
有个需要注意的细节
const 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));
p1会resolved,p2首先会rejected,但是p2有自己的catch方法,该方法返回的是一个新的 Promise 实例,p2指向的实际上是这个实例。该实例执行完catch方法后,也会变成resolved,导致Promise.all()方法参数里面的两个实例都会resolved,因此会调用then方法指定的回调函数,而不会调用catch方法指定的回调函数。
如果p2没有自己的catch方法,就会调用Promise.all()的catch方法。
10. Promise.allSettled()用法
方法返回一个promise,该promise在所有给定的promise已被解析或被拒绝后解析,并且每个对象都描述每个promise的结果
接受的结果与入参时的promise实例一一对应,且结果的每一项都是一个对象,告诉你结果和值,对象内都有一个属性叫“status”,用来明确知道对应的这个promise实例的状态(fulfilled或rejected),fulfilled时,对象有value属性,rejected时有reason属性,对应两种状态的返回值
const resolved = Promise.resolve(42); const rejected = Promise.reject(-1); const allSettledPromise = Promise.allSettled([resolved, rejected]); allSettledPromise.then(function (results) { console.log(results); }); // [ // { status: 'fulfilled', value: 42 }, // { status: 'rejected', reason: -1 } // ]
11.Promise.race()用法
Promise.race(iterable) 方法返回一个 promise
,一旦迭代器中的某个promise
解决或拒绝,返回的 promise
就会解决或拒绝
Promse.race就是赛跑的意思,意思就是说,Promise.race([p1, p2, p3])里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。
let p1 = new Promise((resolve, reject) => { setTimeout(() => { resolve('success') },1000) }) let p2 = new Promise((resolve, reject) => { setTimeout(() => { reject('failed') }, 500) }) Promise.race([p1, p2]).then((result) => { console.log(result) }).catch((error) => { console.log(error) // 打开的是 'failed' })
原理是简单,但是在实际运用中还没有想到什么使用场景会用到
const promises = [ Promise.reject('ERROR A'), Promise.reject('ERROR B'), Promise.resolve('result'), ] Promise.any(promises).then((value) => { console.log('value: ', value) }).catch((err) => { console.log('err: ', err) }) 如果所有传入的 promises 都失败: const promises = [ Promise.reject('ERROR A'), Promise.reject('ERROR B'), Promise.reject('ERROR C'), ] Promise.any(promises).then((value) => { console.log('value:', value) }).catch((err) => { console.log('err:', err) console.log(err.message) console.log(err.name) console.log(err.errors) })
13.总结
promise主要是为了解决js中多个异步回调难以维护和控制的问题.
实例:
setTimeout(function () { console.log('我'); setTimeout(function () { console.log('要'); setTimeout(function () { console.log('下'); setTimeout(function () { console.log('班'); }, 1000); }, 1000); }, 1000); }, 1000);
回调函数的嵌套有点多,如果有更多的回调函数,代码的可读性和可维护性都大大降低,如果使用Promise去实现这个效果,虽然可能代码不会减少,甚至更多,但是却增强了其可读性和可维护性。
function getStr1() { return new Promise(function (resolve, reject) { setTimeout(function () { resolve('我'); }, 1000); }); } function getStr2() { return new Promise(function (resolve, reject) { setTimeout(function () { resolve('要'); }, 1000); }); } function getStr3() { return new Promise(function (resolve, reject) { setTimeout(function () { resolve('下'); }, 1000); }); } function getStr4() { return new Promise(function (resolve, reject) { setTimeout(function () { resolve('班'); }, 1000); }); } getStr1().then(function (data) { console.log(data); return getStr2(); }).then(function (data) { console.log(data); return getStr3(); }).then(function (data) { console.log(data); return getStr4(); }).then(function (data) { console.log(data); })
这样一级一级下去实现了链式调用,虽然代码量增加了,但比起前面的层层嵌套,显然这种方式代码更易读更易维护