第 1 章 Promise的理解和使用
1.1 Promise 是什么?
1.1.1 理解
抽象表达
Promise
是一门新的技术(ES6
规范);
Promise
是JS
中进行异步编程的新解决方案,备注:旧方案是单纯使用回调函数.
异步编程: fs文件操作、数据库操作、ajax、定时器。
具体表达
从语法上来说:
Promise
是一个构造函数;
从功能上来说:Promise
对象用来封装一个异步操作并可以获取其成功/失败的结果值。
1.1.2 promise 的状态改变
pending
变为resolved
;pending
变为rejected
;Promise
初始状态是pending
。
说明: 只有这 2 种, 且一个
promise
对象只能改变一次,无论变为成功还是失败, 都会有一个结果数据成功的结果数据一般称为value
, 失败的结果数据一般称为reason
。
PromiseState
: Promise
的状态 是实例对象中的一个属性 可能的值有:
pending
(未决定的);resolved
(fullfilled
成功的);reject
(rejected
失败的);
状态的改变只会从 pending => fullfilled 或者 pending => rejected
;
PromiseResult
是实例对象中的另一个属性 保存着对象【成功/失败】的结果 保存异步任务成功或者失败的结果
resolve
;reject
;
1.1.3 Promise初体验 : 抽奖案例
- 页面上有一个按钮当点击按钮之后1秒 ,显示是否中奖,不管中没中奖都给出相应的提示信息。
/**
* 生成随机数
* */
function rand(m, n) {
return Math.ceil(Math.random() * (n - m + 1)) + m - 1;
}
/**
* 1. 点击按钮,1s后显示是否 中奖(30%概率中奖)
* 1.1 如果中奖弹出恭喜恭喜,奖品...
* 1.2 如果未中奖弹出,再接再厉
*/
// 1. 传统回调函数的方式实现
let btn = document.querySelector('#btn');
/*btn.addEventListener('click', () => {
setTimeout(function () {
let n = rand(1, 100);// 获取 1~100的随机数
if (n <= 30) {
alert('恭喜恭喜,您中奖了!');
} else {
alert('很遗憾,请再来一次!');
}
}, 1000);
});*/
// 2. Promise 形式实现
/**
* resolve 解决 函数类型的数据
*
* reject 拒绝 函数类型的数据
*/
btn.addEventListener('click', () => {
let p = new Promise((resolve, reject) => {
setTimeout(function () {
let n = rand(1, 100);// 获取 1~100的随机数
if (n <= 30) {
resolve(n);
} else {
reject(n);
}
}, 1000);
});
/**
* 1. 需求,将中奖的数字显示出来
*/
p.then(value => {
alert('恭喜恭喜,您中奖了! 将号码是 => ' + value);
},
reason => {
alert('很遗憾,请再来一次!');
})
});
1.1.4 promise 的基本流程
1.2 为什么要用 Promise?
1.2.1 指定回调函数的方式更加灵活
旧的: 必须在启动异步任务前指定。
promise
: 启动异步任务 => 返回promie
对象 => 给promise
对象绑定回调函数(甚至可以在异步任务结束后指定/多个)。
1.2.2 支持链式调用, 可以解决回调地狱问题
1. 什么是回调地狱?
答:回调函数嵌套调用, 外部回调函数异步执行的结果是嵌套的回调执行的条件。
2. 回调地狱的缺点?
不便于阅读
不便于异常处理
3. 解决方案?
promise 链式调用
4. 终极解决方案?
async/await
1.2.3 Promise封装 Ajax请求,点击按钮发送请求
Promise封装ajax请求
Promise封装ajax请求
1.2.4 Promise封装ajax请求,传递参数返回一个Promise对象
Promise封装ajax请求
1.3 如何使用Promise
1.3.1 API
Promise 构造函数: Promise (excutor) {};
executor
函数: 执行器(resolve, reject) => {}
;resolve
函数: 内部定义成功时我们调用的函数value => {}
;reject
函数: 内部定义失败时我们调用的函数reason => {}
说明:executor
会在Promise
内部立即同步调用,异步操作在执行器中执行。
/**
* Promise 构造函数的 参数就是一个执行器,执行器函数是在Promise中同步调用的
* @type {Promise}
*/
let p = new Promise((resolve, reject) => {
// 此函数内部的代码是同步执行的
console.log(11); // 第一打印
// 修改Promise对象的状态
reject('error');
});
console.log(22); // 第二打印
/**
* 都是函数类型的参数
*/
p.then(value => {
},
reason => {
});
/**
* catch只能指定失败的回调
*/
p.catch(reason => {
console.log(reason);
});
Promise.prototype.then 方法: (onResolved, onRejected) => {}
onResolved
函数: 成功的回调函数(value) => {}
;onRejected
函数: 失败的回调函数(reason) => {}
;
说明: 指定用于得到成功 value 的成功回调和用于得到失败 reason 的失败回调返回一个新的 promise 对象
Promise.prototype.catch 方法: (onRejected) => {}
-
onRejected
函数: 失败的回调函数(reason) => {}
。
说明: then()的语法糖, 相当于: then(undefined, onRejected)
。
Promise.resolve 方法: (value) => {}
value
: 成功的数据或promise
对象。
说明: 返回一个成功/失败的 promise 对象。
/**
* Promise对象的resolve方法
* 1. 接收一个参数返回一个成功和失败的Promise对象
* 2. 如果传递的参数为非Promise类型的对象,则返回的结果为成功的Promise对象
* 3. 如果传递的参数是一个Promise对象,则参数的结果决定了 resolve的结果
*/
let p1 = Promise.resolve('成功');
console.log(p1);
/**
* 传递的参数是一个Promise对象 返回是成功
* @type {Promise}
*/
let p2 = Promise.resolve(new Promise((resolve, reject) => {
resolve('OK');
}));
console.log(p2);
/**
* 传递的参数是一个Promise对象 返回的是失败
* @type {Promise}
*/
let p3 = Promise.resolve(new Promise((resolve, reject) => {
reject('Error');
}));
console.log(p3);
p3.catch(reason => {
console.log(reason); // Error 对处理失败进行处理
});
Promise.reject 方法: (reason) => {}
-
reason
: 失败的原因。
说明: 返回一个失败的 promise 对象。
/**
* Promise中的reject方法
* 1. reason:失败的原因
* 2. 如果传递的是一个非Promise对象,其结果是一个失败的Promise
* 3. 如果传递的是一个Promise,不管是返回成功的值还是失败的值其处理结果都是失败的Promise
*/
let p1 = Promise.reject('失败!');
console.log(p1);
let p2 = Promise.reject(new Promise((resolve, reject) => {
resolve('成功!');
}));
console.log(p2);
let p3 = Promise.reject(new Promise((resolve, reject) => {
reject('Error');
}));
console.log(p3);
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('resolve!');
let result = Promise.all([p1, p2, p3]); // 只有三个同时返回成功才会成功,否则将返回失败
console.log(result);
Promise.race 方法: (promises) => {}
-
promises
: 包含 n 个promise
的数组。
说明: 返回一个新的 promise, 第一个完成的 promise 的结果状态就是最终的结果状态。
/**
* 返回一个新的Promise,第一个完成的Promise的结果状态就是最终的状态
*/
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('OK!');
}, 1000);
});
let p2 = Promise.resolve('Success!');
let p3 = Promise.resolve('resolve!');
let result = Promise.race([p1, p2, p3]); // 只有三个同时返回成功才会成功,否则将返回失败
console.log(result);
Promise 封装读取文件函数
/**
* 封装一个函数 mineReadFile读取文件内容
* 参数: path文件路径
* 返回: Promise对象
*/
const fs = require('fs');
function mineReadFile(path) {
return new Promise((resolve, reject) => {
fs.readFile(path, (err, data) => {
if (err) reject(err);
resolve(data);
});
});
}
mineReadFile('../resources/context.txt').then(value => {
console.log(value.toString());
},
reason => {
console.log(reason);
});
const fs = require('fs');
/**
* 1. 使用传统的方式
*/
fs.readFile('../resources/context.txt', (err, data) => {
if (err) throw new Error(err);
console.log(data.toString());
});
// 2. 使用Promise的方式
let p = new Promise((resolve, reject) => {
fs.readFile('../resources/context.txt', (err, data) => {
if (err) reject(err);
resolve(data.toString());
});
});
p.then(value => {
console.log('------------------------------------------------------------------------');
console.log(value.toString());
},
reason => {
console.log(reason);
});
util.promisify简化Promise
-
util.promisify
是NodeJS
的API
。 参考网址
/**
* 传入一个遵循常见的错误优先的回调风格的函数(即以(err,value) => 回调作为最后一个参数),并返回一个Promise的版本
*/
// 引入util 模块
const util = require('util'); // 引入util这个模块
// 引入fs模块
const fs = require('fs');
// 返回一个新函数
/**
* util.promisify
*/
let mineReadFile = util.promisify(fs.readFile);
mineReadFile('../resources/context.txt').then(value => {
console.log(value.toString());
});
1.3.2 promise 的几个关键问题
如何改变 promise 的状态?
开始
Promise
对象的初始化状态是pending
。resolve(value)
: 如果当前是pending
就会变为resolved
。reject(reason)
: 如果当前是pending
就会变为rejected
。抛出异常: 如果当前是
pending
就会变为rejected
。
/**
* 代码中如何修改Promise对象的状态
*/
let p = new Promise((resolve,reject) => {
// 1. 调用resolve函数 将对象的状态 从 pending 修改为 fullfilled
// resolve('OK');
// 2. 调用reject函数 pending => rejected
// reject('Error');
// 3. 抛出异常
throw new Error('出问题了!');
});
一个 promise 指定多个成功/失败回调函数, 都会调用吗?
- 当
promise
改变为对应状态时都会调用。
/**
* 为一个Promise对象指定多个回调,当他们的状态改变的时候都会调用,如果一直是pending状态的话就不会调用其回调
*/
let p = new Promise((resolve, reject) => {
resolve('OK');
});
p.then(value => {
console.log(value);
});
p.then(value => {
alert(value);
});
改变 promise 状态和指定回调函数谁先谁后?
都有可能, 正常情况下是先指定回调再改变状态, 但也可以先改状态再指定回调。
如何先改状态再指定回调?
在执行器中直接调用
resolve()/reject()
。
延迟更长时间才调用then()
。
- 什么时候才能得到数据?
如果先指定的回调, 那当状态发生改变时, 回调函数就会调用, 得到数据。
如果先改变的状态, 那当指定回调时, 回调函数就会调用, 得到数据。
promise.then()返回的新 promise 的结果状态由什么决定?
简单表达: 由 then()指定的回调函数执行的结果决定。
详细表达:
如果抛出异常, 新 promise 变为 rejected, reason 为抛出的异常。
如果返回的是非 promise 的任意值, 新 promise 变为 resolved, value 为返回的值。
如果返回的是另一个新 promise, 此 promise 的结果就会成为新 promise 的结果。
promise 如何串连多个操作任务
promise 的 then()返回一个新的 promise, 可以开成 then()的链式调用。
通过 then 的链式调用串连多个同步/异步任务。
promise 异常传透?
当使用 promise 的 then 链式调用时, 可以在最后指定失败的回调。
前面任何操作出了异常, 都会传到最后失败的回调中处理。
中断 promise 链?
当使用 promise 的 then 链式调用时, 在中间中断, 不再调用后面的回调函数。
办法: 在回调函数中返回一个 pendding 状态的 promise 对象。
第 2 章 自定义(手写) Promise
2.1 函数版本
/**
* 执行多个回调的实现
* @param executor
* @constructor
*/
function Promise(executor) {
// 3. 添加属性
this.PromiseState = 'pending';
this.PromiseResult = null;
// 声明一个属性
this.callBacks = [];
// 预先保存this的值
const _this = this;
function resolve(data) {
// 修改对象的状态 PromiseState 属于实例对象的属性
// 设置结果值 PromiseResult 属于实例对象的属性
// 对象状态只允许修改一次
if (_this.PromiseState !== 'pending') return;
_this.PromiseState = 'fullfilled';
_this.PromiseResult = data;
// 在异步任务的时候,调用回调函数的时机
// 循环遍历执行函数
setTimeout(() => {
_this.callBacks.forEach(item => {
item.onResolved(data);
});
});
}
function reject(data) {
// 对象状态只允许修改一次
if (_this.PromiseState !== 'pending') return;
_this.PromiseState = 'rejected';
_this.PromiseResult = data;
// 在异步任务的时候,调用回调函数的时机
/**
* 循环遍历执行多个函数
*/
setTimeout(() => {
_this.callBacks.forEach(item => {
item.onRejected(data);
});
});
}
// 执行器函数式同步调用的
// 调用执行器函数
try {
executor(resolve, reject);
} catch (e) {
reject(e);
}
}
// 1. 添加then方法
Promise.prototype.then = function (onResolved, onRejected) {
// 保存this的值
const _this = this;
// 判断回调函数的参数 是否为一个函数类型
if (typeof onRejected !== 'function') {
/**
* 实现异常穿透的原理
* @param reason
*/
onRejected = reason => {
throw reason;
}
}
/**
* Promise实现值传递
*/
if (typeof onResolved !== 'function') {
onResolved = value => value;
}
return new Promise((resolve, reject) => {
function callBack(type) {
/**
* 当出现异常的时候
*/
try {
// 获取回调函数的执行结果
let result = type(_this.PromiseResult);
if (result instanceof Promise) {
// 如果是Promise类型的对象
result.then(value => {
resolve(value);
},
reason => {
reject(reason);
})
} else {
// 返回非Promise对象的时候状态为成功
resolve(result);
}
} catch (e) {
reject(e);
}
}
// 调用回调函数 成功失败的状态是 由PromiseState决定
if (this.PromiseState === 'fullfilled') {
setTimeout(() => {
callBack(onResolved);
});
}
if (this.PromiseState === 'rejected') {
setTimeout(() => {
callBack(onRejected);
});
}
// 判断 pending的状态
if (this.PromiseState === 'pending') {
// 回调的执行时机?
// 保存回调函数 重要 重要 重要
// 8. 实现执行多个回调
this.callBacks.push({
onResolved: function () {
callBack(onResolved);
},
onRejected: function () {
callBack(onResolved);
}
});
}
});
};
// 12. 添加catch方法
Promise.prototype.catch = function (onRejected) {
/**
* 直接调用then方法
*/
return this.then(undefined, onRejected);
};
// 13. 添加Promise.resolve方法
/**
* Promise对象的 resolve 方法
* @param value
* @returns {Promise}
*/
Promise.resolve = function (value) {
return new Promise((resolve, reject) => {
// 判断value 的类型
if (value instanceof Promise) {
value.then(value => {
resolve(value);
}, reason => {
reject(reason);
});
} else {
resolve(value);
}
});
};
// 14. 添加 reject 方法
/**
* 为Promise对象添加 reject 方法
* @param reason
* @returns {Promise}
*/
Promise.reject = function (reason) {
return new Promise((resolve, reject) => {
reject(reason);
});
};
// 15. 添加all方法
/**
* 为Promise对象添加 all 方法
* @param promises
* @returns {Promise}
*/
Promise.all = function (promises) {
// 定义一个计数变量 计算当前Promise成功的个数
let count = 0;
let arr = [];
return new Promise((resolve, reject) => {
// 遍历 promises
for (let i = 0; i < promises.length; i++) {
promises[i].then(value => {
// 得知对象的状态是成功
// 状态成功就会走到该函数中 进行count++ 操作
count++;
// 将当前promise对象成功的结果存到一个数组中保持原有的顺序
arr[i] = value;
// 判断
if (count === promises.length) {
// 修改此时Promise的状态
resolve(arr);
}
},
reason => {
reject(reason);
});
}
});
};
// 16 . race 方法 接受一个参数 Promise 对象数组 其结果由数组中最先改变状态的那个值决定
/**
* Promise 对象的 race 方法
* @param promises
* @returns {Promise}
*/
Promise.race = function (promises) {
return new Promise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
promises[i].then(
value => {
resolve(value);
},
reason => {
reject(reason);
}
);
}
});
};
// 17. 使用 setTimeOut 包裹代码将其转换为异步执行的代码
2.2 Class版本
class Promise {
/**
* 执行多个回调的实现
* @param executor
* @constructor
*/
constructor(executor) {
// 3. 添加属性
this.PromiseState = 'pending';
this.PromiseResult = null;
// 声明一个属性
this.callBacks = [];
// 预先保存this的值
const _this = this;
function resolve(data) {
// 修改对象的状态 PromiseState 属于实例对象的属性
// 设置结果值 PromiseResult 属于实例对象的属性
// 对象状态只允许修改一次
if (_this.PromiseState !== 'pending') return;
_this.PromiseState = 'fullfilled';
_this.PromiseResult = data;
// 在异步任务的时候,调用回调函数的时机
// 循环遍历执行函数
setTimeout(() => {
_this.callBacks.forEach(item => {
item.onResolved(data);
});
});
}
function reject(data) {
// 对象状态只允许修改一次
if (_this.PromiseState !== 'pending') return;
_this.PromiseState = 'rejected';
_this.PromiseResult = data;
// 在异步任务的时候,调用回调函数的时机
/**
* 循环遍历执行多个函数
*/
setTimeout(() => {
_this.callBacks.forEach(item => {
item.onRejected(data);
});
});
}
// 执行器函数式同步调用的
// 调用执行器函数
try {
executor(resolve, reject);
} catch (e) {
reject(e);
}
}
// then 方法
then(onResolved, onRejected) {
// 保存this的值
const _this = this;
// 判断回调函数的参数 是否为一个函数类型
if (typeof onRejected !== 'function') {
/**
* 实现异常穿透的原理
* @param reason
*/
onRejected = reason => {
throw reason;
}
}
/**
* Promise实现值传递
*/
if (typeof onResolved !== 'function') {
onResolved = value => value;
}
return new Promise((resolve, reject) => {
function callBack(type) {
/**
* 当出现异常的时候
*/
try {
// 获取回调函数的执行结果
let result = type(_this.PromiseResult);
if (result instanceof Promise) {
// 如果是Promise类型的对象
result.then(value => {
resolve(value);
},
reason => {
reject(reason);
})
} else {
// 返回非Promise对象的时候状态为成功
resolve(result);
}
} catch (e) {
reject(e);
}
}
// 调用回调函数 成功失败的状态是 由PromiseState决定
if (this.PromiseState === 'fullfilled') {
setTimeout(() => {
callBack(onResolved);
});
}
if (this.PromiseState === 'rejected') {
setTimeout(() => {
callBack(onRejected);
});
}
// 判断 pending的状态
if (this.PromiseState === 'pending') {
// 回调的执行时机?
// 保存回调函数 重要 重要 重要
// 8. 实现执行多个回调
this.callBacks.push({
onResolved: function () {
callBack(onResolved);
},
onRejected: function () {
callBack(onResolved);
}
});
}
});
}
// catch 方法
catch(onRejected) {
/**
* 直接调用then方法
*/
return this.then(undefined, onRejected);
}
/**
* Promise对象的 resolve 方法
* @param value
* @returns {Promise}
*/
static resolve(value) {
return new Promise((resolve, reject) => {
// 判断value 的类型
if (value instanceof Promise) {
value.then(value => {
resolve(value);
}, reason => {
reject(reason);
});
} else {
resolve(value);
}
});
};
/**
* 为Promise对象添加 reject 方法
* @param reason
* @returns {Promise}
*/
static reject(reason) {
return new Promise((resolve, reject) => {
reject(reason);
});
};
/**
* 为Promise对象添加 all 方法
* @param promises
* @returns {Promise}
*/
static all(promises) {
// 定义一个计数变量 计算当前Promise成功的个数
let count = 0;
let arr = [];
return new Promise((resolve, reject) => {
// 遍历 promises
for (let i = 0; i < promises.length; i++) {
promises[i].then(value => {
// 得知对象的状态是成功
// 状态成功就会走到该函数中 进行count++ 操作
count++;
// 将当前promise对象成功的结果存到一个数组中保持原有的顺序
arr[i] = value;
// 判断
if (count === promises.length) {
// 修改此时Promise的状态
resolve(arr);
}
},
reason => {
reject(reason);
});
}
});
};
/**
* Promise 对象的 race 方法
* @param promises
* @returns {Promise}
*/
static race(promises) {
return new Promise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
promises[i].then(
value => {
resolve(value);
},
reason => {
reject(reason);
}
);
}
});
};
}