Promise
语义通过Promise对象假装拿到异步请求在未来时间后,返回的值。所以说Promise又是一个容器,用于存储未来返回的值。
关键点Promise是状态管理机。通过状态变化,实现异步回调函数的执行。
关键点Promise将异步请求和回调函数分离开来。
关键点使用resolve函数指针,一旦在异步函数里执行了该函数,就会改变Promise的状态,立即执行then方法里的回调函数。
function timeout(ms) {
return new Promise((resolve, reject) => {
setTimeout(resolve, ms);
})
}
timeout(1000)
.then(() => {
console.log(1);
})
function imageLoad(url) {
return new Promise((resolve, reject) => {
//创建一个image元素
var img = new Image();
img.src = url;
resolve(img);
})
}
imageLoad('https://i2.hdslb.com/bfs/face/337351c343b05f0ee735e0918f88862dc645feea.jpg@96w_96h_1c_1s.webp')
.then((val) => {
document.body.appendChild(val);
})
function axios(url) {
var promise = new Promise((resolve, reject) => {
var xhr = new XMLHttpRequest();
xhr.open("get", url, true);
xhr.send();
xhr.onload = function () {
resolve(this.status);
}
});
return promise;
}
axios('http://localhost:3000/').then((data) => {
console.log(data);
})
关键点Promise绑定DOM事件只会执行一次回调函数,不会重复执行。因为Promise的状态只会改变一次。
var oDiv = document.getElementsByClassName('div')[0];
var promise = new Promise( (resolve, reject) => {
oDiv.addEventListener('click', ()=>{
resolve();
} ,false)
} ).then(() => {
console.log(3);
})
关键点状态传递。p2的状态改变,由p1的状态决定。
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error('error'));
}, 1000);
});
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(p1);
}, 2000);
});
p2.then((data) => {
console.log(data);
}).catch((err) => {
console.log(err);
})
promise.prototype.then( )
关键点then方法里的函数才是需要异步执行的回调函数。而promise实例化对象就是一个对象,用于监听状态的变化,一旦变化执行then方法。
- then中的回调函数的没有返回值。then( )方法的返回值是Promise对象(resolve状态,容器中的未来值就是undefined)。 ```javascript var promise = new Promise((resolve, reject) => { resolve(1) });
promise .then((val) => { console.log(val); //1 }) .then((val) => { console.log(val); //undefined })
- then中的回调函数的返回值是一个值val时,then( )方法的返回值是Promise对象(resolve状态,容器中的未来值就是val)。
```javascript
var promise = new Promise((resolve, reject) => {
resolve(1)
});
promise
.then((val) => {
console.log(val); //1
return 2;
})
.then((val) => {
console.log(val); //2
})
- then中的回调函数的返回值是promise对象,then( )方法的返回值就是这个Promise对象。
promise.prototype.catch( )
等同于then的失败的回调的写法。
关键点promise对象的错误,会一直传递,直到被catch捕获。
关键点如果没有catch方法,promise对象中的错误是不会被捕获。同时错误不会影响到外部代码,通俗的说法就是“Promise 会吃掉错误”。const promise = new Promise((resolve, reject) => { setTimeout(()=>{resolve(1)}, 1000); }) .then((res) => console.log(res)) .catch((err)=> console.log(err))
promise.prototype.all( )
```javascript //管理promise对象p1,p2,p3。只有所有的promise实例变成fulfilled状态时,all才会调用then回调函数。 //只有有一个promise状态是reject,就会执行catch const p1 = new Promise((resolve, reject) => { setTimeout(()=>{resolve(1)}, 1000); }) const p2 = new Promise((resolve, reject) => { setTimeout(()=>{resolve(2)}, 2000); }) const p3 = new Promise((resolve, reject) => { setTimeout(()=>{resolve(3)}, 3000); })
const all = Promise.all([p1, p2, p3]); all.then((res) => { console.log(res); //[1,2,3] }).catch()
//解构语法 const all = Promise.all([p1, p2, p3]); all.then(([a, b, c]) => { console.log(a); //1 })
<a name="EDZaS"></a>
## promise.prototype.race( )
管理多个promise对象,但是只会返回第一个改变状态的promise对象的结果。
<a name="js29F"></a>
## promise.prototype.finally( )
不管promise对象的状态是什么,最后都会执行里面的回调函数。
<a name="hnTPa"></a>
## promise.prototype.resolve( )
将一个参数直接转换为fulfilled状态的promise对象。
- 参数是一个promise实例,直接返回这个对象
- 参数是原始值,返回一个新的 Promise 对象,状态为resolved,传递的值就是这个原始值。
- 没有参数。返回一个新的 Promise 对象,状态为resolved。说明可以立即执行then里面的回调函数。
关键点立即resolve()的 Promise 对象,是在本轮“事件循环”(event loop)的结束时执行,而不是在下一轮“事件循环”的开始时。
- 参数是thenable对象。具有then方法的对象
```javascript
const thenable = {
then: function (resolve, reject) {
resolve(2)
}
}
const p = Promise.resolve(thenable);
p.then((res) => {
console.log(res);
})
promise.prototype.reject( )
将一个参数直接转换为rejected状态的promise对象。
手写promise
- 问题:对于执行器中的如果有异步函数,发现promise.then( )没有执行?
由于事件循环机制,同步代码promise.then( )方法优先执行结束后,才会执行异步回调函数里面的resolve( )语句。
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
constructor(executor) {
this.status = PENDING;
this.value = undefined;
this.reason = undefined;
let resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
}
}
let reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
}
}
try {
executor(resolve, reject)
} catch (error) {
reject();
}
}
then(onFulfilled, onRejected) {
if (this.status === FULFILLED) {
onFulfilled();
}
if (this.status === REJECTED) {
onRejected();
}
}
}
var promise = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve();
})
});
promise.then(() => {
console.log('success')
})
- 对于异步回调代码,由于事件循环resolve( )要等到所有同步代码执行完再执行,因此then最开始执行的时候肯定处于pending状态。这时then方法不是什么都不做。
- then方法:异步回调函数push到构造函数的数组里。
- resolve函数的执行,增加一条语句来执行,收集到的回调函数。
关键点解决回调地狱的方法是将异步回调函数分开来写。由promise内部将分开的代码再组合起来。
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
constructor(executor) {
this.status = PENDING;
this.value = undefined;
this.reason = undefined;
this.onResolvedCallbacks = [];
this.onRejectedCallbacks = [];
let resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
this.onResolvedCallbacks.forEach(fn => fn());
}
}
let reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
this.onRejectedCallbacks.forEach(fn => fn());
}
}
try {
executor(resolve, reject)
} catch (error) {
reject();
}
}
then(onFulfilled, onRejected) {
if (this.status === FULFILLED) {
onFulfilled(this.value);
}
if (this.status === REJECTED) {
onRejected(this.value);
}
//对于异步代码,then方法会先执行,此时还处于pending状态,先收集依赖,等过段时间执行resolve()时,取出来执行。而不是在then方法里面执行
if (this.status === PENDING) {
this.onResolvedCallbacks.push(() => {
onFulfilled(this.value);
});
this.onRejectedCallbacks.push(() => {
onRejected(this.value);
})
}
}
}
var promise = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve();
})
});
promise.then(() => {
console.log('success')
})
- promise 的优势在于可以链式调用。在我们使用 Promise 的时候,当 then 函数中 return 了一个值,不管是什么值,我们都能在下一个 then 中获取到,这就是所谓的then 的链式调用。而且,当我们不在 then 中放入参数,例:promise.then().then(),那么其后面的 then 依旧可以得到之前 then 返回的值,这就是所谓的值的穿透
参考连接
https://es6.ruanyifeng.com/#docs/promise
https://zhuanlan.zhihu.com/p/183801144