一、Promise 特点
- 对象的状态不受外界影响。Promise 对象代表一个异步操作,有三种状态:
- pending: 初始状态,不是成功或失败状态。
- resolve/fulfilled: 意味着操作成功完成。
- rejected: 意味着操作失败。
- 一旦状态改变,就不会再变。
- pendding=>resolve/fulfilled;
- pendding=>rejected;
二、Promise 优缺点
- 优点:
- 链式调用,解决回调地狱问题
- 指定回调函数的方式更加灵活
- 缺点:
- 无法取消 Promise,一旦新建它就会立即执行,无法中途取消。
- 如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。
- 当处于 Pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)
三、主要异步任务
- Ajax请求
- setTimeout定时器
- fs文件流操作等
- ...
四、Promise 的简单使用
// fs 异步操作
const fs = require('fs');
const pro_obj = new Promise((resolve, rejects) => {
fs.readFile('./demo.txt', (err, data) => {
if (err) rejects(err);
resolve(data);
});
});
pro_obj.then(
(value) => {
console.log(value.toString());
},
(err) => {
console.log(err);
}
);
五、 Promise API
- Promise 构造函数: Promise(excutor){}
- executor 函数:执行器 (resolve,reject)=>{}
- resolve 函数:内部定义成功时调用的函数 value=>{}
- reject 函数: 内部定义失败时调用的函数 reson=>{} 注:执行器函数会在Promise内部同步调用,异步操作再执行器中执行
- Promise.prototype.then 方法: (onResolved,onRejected)=>{}
- onResolved 函数: 成功的回调函数 (value)=>{}
- onRejected 函数:失败的回调函数 (reson)=>{} 注:成功或者失败的回调函数返回的是一个新的Promise对象
- Promise.prototype.catch方法: (onRejected)=>{}
- onRejected 函数:失败的回调函数 (reson)=>{}
- Promise.resolve 方法:(value)=>{}
- value:成功的数据或者Promise对象 (返回一个成功/失败的的Promise对象)
let p1 = Promise.resolve(521)
// 传入的参数为非Promise类型的对象,则返回的结果是成功的Promise对象
console.log(p1) //Promise { 521 }
// 传入的参数为Promise类型的对象,参数的结果决定了resolve的结果
let p2 = Promise.resolve(new Promise((resolve, reject) => {
resolve('ok')
}))
console.log(p2) // Promise { 'ok' }
- Promise.reject方法:(reson)=>{}
- reson: 返回失败的原因
let p1 = Promise.reject(521)
console.log(p1) //Promise { <rejected> 521 }
let p2 = Promise.reject(new Promise((resolve, reject) => {
resolve(521)
}))
console.log(p2) //Promise { <rejected> Promise { 521 } }
// 即使是实例对象的回调函数是成功的,reject返回的还是失败的结果
- Promise.all 方法:(promises)=>{}
- promises:包含n个promise的数组
注:返回一个新的promise,只有所有的promise都成功才算成功,只要一个失败了就直接失败
let p1 = new Promise((resolve, reject) => {
resolve('ok');
});
let p2 = Promise.resolve('success');
let p3 = Promise.resolve('okkkk');
let p4 = Promise.reject('error');
const result = Promise.all([p1, p2, p3]);
const err = Promise.all([p1, p2, p3, p4]);
console.log(result);
console.log(err);
全部成功结果 result:
有一个失败结果 err:
- Promise.race 方法: (promises)=>{}
- promises:包含n个promise的数组
注:返回一个新的promise对象,是第一个成功的的promise的结果状态
let p1 = new Promise((resolve, reject) => {
reject('ok');
});
let p2 = Promise.reject('success');
let p3 = Promise.resolve('okkkk');
const result = Promise.race([p1, p2, p3]);
console.log(result);
六、Promise关键问题
1. 修改Promise状态的方法
- 执行reject方法 pending=> reject
- 执行resolve方法 pending =>resolve
- 丢出错误throw pending=>reject
2.能否执行多个回调
当promise状态改变时,它所定义的多个成功/失败的回调函数都会执行
3.改变promise的状态和指定回调函数谁先谁后(resolve、reject先执行还是then、catch先执行)
- 都有可能,正常情况下是先指定回调再改变状态,但也可以先改变状态再指定回调(看执行器内部的任务队列是同步任务还是异步任务,) 如何先改变状态再指定回调
- 在执行器中直接调用resolve或者reject
- 延迟更长时间才调用then() 什么时候拿到数据
- 如果先指定的回调,那么当状态改变时,回调函数就会调用,就能得到数据
- 如果先改变的状态,那当指定回调是,回调函数就会调用,得到数据
4.Promise then 方法的返回的新的promise的结果状态由什么决定?
-
简单表达: 由then方法指定的回调函数执行的结果决定
-
详细表达:
a. 如果抛出异常,新的Promise变为rejected,reson为抛出的异常
b. 如果返回的是非promise的任意值,新promise变为resolved,value为返回的值
c. 如果返回的是另外一个新的Promise ,此promise的结果就会成为新的promise的结果
5. Promise如何串联多个操作任务
可以写成then方法的链式调用(原理:promise的then方法返回的还是promise对象)
6. Promise 异常穿透
当使用promise的then的链式调用时,可以在最后指定失败的回调函数catch,当前面任何操作出了异常都会被传到追后失败的回调中处理
7. 中断promise链
当使用promise的then链式调用时,在中间中断,不再调用后面的回调函数。方法:在回调函数中返回一个pendding状态的promise对象
七、手写源码
/**
* 1. 搭建Promise基础架构
* a.Promise接受executor作为参数传入
* b.设置内置属性[PromiseStatus、PromiseResult]和方法[resolve、reject]
* Ⅰ. PromiseStatus有三种状态,分别为pending,fulfilled,rejected,状态只能改变一次,pending=>fuifilled pending=>rejected
* Ⅱ. PromiseResult 执行的结果保存在PromiseResult中
* c. 使用try catch来获取executor中抛出的错误,并返回Promise对象处理抛出的异常
* 2. then方法
* a. then方法执行回调函数需要有条件,根据状态执行对应的回调函数,函数的实参是存储在PromiseResult的值
* b. 处理异步状态,实例对象内部为异步函数,状态是pending ,then方法的回调没有等到pending状态的改变,导致then方法不执行,解决方法:定义callbacks回调数组
* 3. 实现catch方法
* 4. 实现resolve、reject方法
* 5. 实现all、race方法
*
* @param {*} executor
*/
function Promise(executor) {
this.PromiseStatus = 'pending';
this.PromiseResult = null;
this.callbacks = [];
resolve = (data) => {
if (this.PromiseStatus !== 'pending') return;
this.PromiseResult = data;
this.PromiseStatus = 'fulfilled';
// 复现原生Promise的异步调用机制
setTimeout(() => {
this.callbacks.forEach((item) => item.onResolved(data));
});
};
reject = (data) => {
if (this.PromiseStatus !== 'pending') return;
this.PromiseResult = data;
this.PromiseStatus = 'rejected';
// 复现原生Promise的异步调用机制
setTimeout(() => {
this.callbacks.forEach((item) => item.onRejected(data));
});
};
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
Promise.prototype.then = (onResolved, onRejected) => {
// 异常穿透
if (typeof onResolved !== 'function') {
onResolved = (data) => data;
}
if (typeof onRejected !== 'function') {
onRejected = (reason) => {
throw reason;
};
}
return new Promise((resolve, reject) => {
const callback = (type) => {
try {
let result = type(this.PromiseResult);
if (result instanceof Promise) {
result.then(
(r) => resolve(r),
(j) => reject(j)
);
} else {
resolve(result);
}
} catch (error) {
reject(error);
}
};
if (this.PromiseStatus === 'fulfilled') {
callback(onResolved);
}
if (this.PromiseStatus === 'rejected') {
callback(onRejected);
}
if (this.PromiseStatus === 'pending') {
this.callbacks.push({
onResolved: callback(onResolved),
onRejected: callback(onRejected),
});
}
});
};
Promise.prototype.catch = function (onRejected) {
return this.then(undefined, onRejected);
};
Promise.resolve = function (value) {
return new Promise((resolve, reject) => {
if (value instanceof Promise) {
value.then(
(r) => {
resolve(r);
},
(j) => {
reject(j);
}
);
} else {
resolve(value);
}
});
};
Promise.reject = function (value) {
return new Promise((resolve, reject) => {
reject(value);
});
};
Promise.all = function (pArray) {
return new Promise((resolve, reject) => {
let count = 0;
let pResult = [];
for (let i = 0; i < pArray.length; i++) {
pArray[i].then(
(v) => {
count++;
pResult[i] = v;
if (count === pArray.length) {
resolve(pResult);
}
},
(j) => {
reject(j);
}
);
}
});
};
Promise.race = function (pArray) {
return new Promise((resolve, reject) => {
for (let i = 0; i < pArray.length; i++) {
pArray[i].then(
(r) => {
resolve(r);
},
(j) => {
reject(j);
}
);
}
});
};