一 基本理论
简单介绍
我们可以任何函数或者是异步函数改为Promise,尤其是异步函数,改为Promise之后可以进行链式调用,增强代码可读性.
原理介绍
- Promise有三种状态,
进行中
,已完成
,已拒绝
,进行中
状态可以更改为已完成
或已拒绝
,经过更改后无法无法继续更改. - Promise的构造函数,需要传入一个函数,这个函数接受两个函数作为参数.如果执行第一个参数,则当前Promise状态会变为
已完成
;如果执行第二参数,则当前Promise状态会变为已拒绝
; - 对于
已完成
的Promise,我们可以通过实例方法then
第一个参数进行回调; - 对于
已拒绝
的Promise,我们可以:(1)通过后续的实例方法catch (2)或是实例方法then的第二个参数 (3)或是try..catch进行捕获.
二 深入挖掘
规范介绍
定义
任何符合Promise规范的对象或函数都可以称为Promise,(Promise A plus 规范地址)
术语
Promise
: 是拥有then
方法的对象或函数,其行为符合规范- 具有then方法(thanable);
- 其值为JavaScript的任何合法值;
- 异常(exception):是使用
throw
语句抛出的一个值; - 原因(reason):表示Promise的拒绝原因;
概念
- Promies的状态
Promise的状态必须为
等待态
(Pending),已完成
(Fulfilled),已拒绝
(Rejected)三种状态之一.等待态
状态可以更改为已完成
或已拒绝
,经过更改后无法无法继续更改.已完成
状态必须包含一个值已拒绝
状态必须包含一个原因
- Promise必须提供一个then方法,以访问当前值和原因
then
方法:接受两个可选参数onFulFilled
,onRejected
,而且他们都是函数,如果不是函数,则忽略他们; 如果onFulfilled
是一个函数,当Promise已完成
之后,会以Promise的值作为第一个参数去调用它; 如果onRejected
是一个函数,当Promise已拒绝
之后,会以Promise的原因作为第一个参数去调用它;
-
仅包含平台代码的执行上下文堆栈之前,不得调用
onFulFilled
,onRejected
; -
onFulFilled
,onRejected
必须作为普通函数调用;(非实例化调用,这样非严格模式下内部this会指向window) -
在同一个Promise中
then
可以被多次调用:
当promise为
已完成
的时候,所有的onFulfilled
需按照其注册的顺序依次调用. 当promsie为已拒绝
的时候,所有的onRejected
需按照其注册的顺序依次调用.
then
方法必须返回一个Promise
对象,在链式调用中:
如果
onFulfilled
或者onRejected
返回一个值,则返回的下一个Promise对象会进入已完成
状态; 如果onFulfilled
或者onRejected
抛出一个异常,则返回的下一个Promise对象会进入已拒绝
状态; 如果onFulfilled
是非函数,且当前Promise状态为已完成
,则返回的下一个Promise状态必须为已完成且以当前Promise的值作为第一个回调参数; 如果onRejected
是非函数,且当前Promise状态为已拒绝
,则返回的下一个Promise状态必须为已拒绝且以当前Promise的拒绝原因作为第一个回调参数;
静态方法介绍
-
Promise.resolve
:返回一个Promise实例,并将它的状态设置为已完成
,并将它传入的参数作为Promise实例的值; -
Promise.reject
:返回一个Promise实例,并将它的状态设置为已拒绝
,并将它传入的参数作为Promise实例的拒绝原因; -
Promise.all
:接受一个数组,其中包含多个Promise实例,然后返回一个Promise实例;- 当传入的所有实例都为已完成状态的时候,返回的Promise为
已完成
状态; - 当传入的实例中都任何一个为已拒绝状态的时候,染回的Promise为
已拒绝
状态;
- 当传入的所有实例都为已完成状态的时候,返回的Promise为
-
Promise.race
:接受一个数组,其中包含多个Promise实例,然后返回一个Promise实例;- 传入的所有Promise实例互为竞争关系
- 返回第一个改变状态的Promise实例的状态作为返回的Promise实例的状态;
三 相关拓展
generator和async/await
ES6+当中,我们可以使用generator和async/await来操作Promise,比Promise来说他们在语法层面的让调用关系显得更加串行
四 代码实践
1. 使用Promise封装异步操作案例
- 类比:传统回调
function ajax(url,success,fail) {
var client = new XMLHttpRequest();
client.open("GET",url);
client.onreadystatechange = function() {
if(this.readyState !== 4) {
return
}
if(this.status === 200) {
success(this.response);
}
else {
fail(new Error(this.statusText));
}
}
client.send();
}
ajax(
'/ajax.json',
function(){
console.log("success!")
},
function(){
console.log("fail!")
}
)
- Promise封装
// Promise封装
function ajaxAsync(url) {
return new Promise(function(resolve,reject) {
var client = new XMLHttpRequest();
client.open();
client.onreadystatechange = function() {
if(this.readyState !== 4) {
return
}
if(this.state === 200) {
resolve(this.response)
}
else {
reject(new Error(this.statusText))
}
}
client.send();
})
}
ajaxAsync('/ajax.json')
.catch(function(){
console.log("fail!")
})
.then(function() {
console.log("success!")
})
2. 链式调用两个Promise
/** @desc define function by Promise */
function promiseCreator() {
return new Promise(function(resolve){
setTimeout(resolve, 1000,'1s');
})
}
function promiseCreator2() {
return new Promise(function(resolve){
setTimeout(resolve, 1000,'2s');
})
}
var promiseCreatorList = [
promiseCreator,
promiseCreator2,
];
/** @desc run by async/await */
async function runPromisesByForOf(list) {
for (const promise of list) {
await promise()
}
}
runPromisesByForOf(promiseCreatorList)
/** @desc run by reduce */
function runPromisesByReduce(list) {
list.reduce(
(prev, cur) => prev.then(cur),
Promise.resolve()
)
}
runPromisesByReduce(promiseCreatorList)
3. 简单封装Promise
功能点: 1.
class Promise {
constructor(executor) {
if (typeof executor !== "function") {
throw new TypeError("Executor of Promise must be a function.")
}
this.init()
try {
executor(this.resolve, this.reject)
}
catch (e) {
this.reject(e)
}
}
init() {
this.value = null;
this.reason = null;
this.status = Promise.PENDING;
this.onFulfilledCbs = [];
this.onRejectedCbs = [];
this.resolve = this.resolve.bind(this);
this.reject = this.reject.bind(this);
}
resolve(val) {
if (this.status === Promise.PENDING) {
this.status = Promise.FULFILLED;
this.value = val;
this.onFulfilledCbs.forEach(fn => fn(this.value))
}
}
reject(err) {
if (this.status === Promise.PENDING) {
this.status = Promise.REJECTED;
this.reason = err;
this.onRejectedCbs.forEach(fn => fn(this.reason))
}
}
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled !== 'function' ? val => val : onFulfilled;
onRejected = typeof onRejected !== 'function' ? reason => { throw reason } : onRejected;
/** 递归构造一个new Promise */
let promise2 = new Promise((resolve, reject) => {
/** 分三种情况 */
if (this.status === Promise.FULFILLED) {
setTimeout(() => {
try {
let x = onFulfilled(this.value);
/** 参数: 调用后返回一个thenable*/
Promise.resolvePromise(promise2, x, resolve, reject)
}
catch (e) {
reject(e)
}
})
}
if (this.status === Promise.REJECTED) {
setTimeout(() => {
try {
let x = onRejected(this.reason);
/** 参数: 调用后返回一个thenable */
Promise.resolvePromise(promise2, x, resolve, reject)
}
catch (e) {
reject(e)
}
})
}
if (this.status === Promise.PENDING) {
/** 如果是PENDING,就就会添加一个包装过后的完成和拒绝函数,结构跟现实调用的一致 */
this.onFulfilledCbs.push(value => {
setTimeout(() => {
try {
let x = onFulfilled(value);
Promise.resolvePromise(promise2, x, resolve, reject)
}
catch (e) {
reject(e)
}
})
})
this.onRejectedCbs.push(reason => {
setTimeout(() => {
try {
let x = onRejected(reason)
Promise.resolvePromise(promise2, x, resolve, reject)
}
catch (e) {
reject(e)
}
})
})
}
})
return promise2
}
catch(onRejected) {
return this.then(null, onRejected);
}
static all(list) {
return new CustomPromise((resolve, reject) => {
let count = 0;
const values = [];
for (const [i, customPromiseInstance] of list.entries()) {
customPromiseInstance
.then(
res => {
values[i] = res;
count++;
if (count === list.length) resolve(values);
},
err => {
reject(err);
}
)
}
});
}
static resolve(val) {
return new CustomPromise(resolve => resolve(val));
}
}
Promise.PENDING = "pending";
Promise.FULFILLED = "fulfilled";
Promise.REJECTED = "rejected";
/** Promise.resolve */
Promise.resolvePromise = function (promise2, x, resolve, reject) {
/** 防止循环引用 */
if (promise2 == x) {
return reject(new TypeError("Chaining cycle detected for promise"))
}
let called;
/** 有效回调函数 */
if (x !== null && typeof x === "object" || typeof x === "function") {
try {
// 有then
let then = x.then;
if (typeof then === "function") {
// 为保证上下文环境绑定自己作为执行上下文.
than.call(
x,
value => {
// 如果已经调用了就不在调用了
if (called) return;
called = true;
// 继续递归调用
Promise.resolvePromise(promise2, value, resolve, reject)
},
err => {
if (called) return;
called = true;
// 拒绝
reject(err)
}
)
} else {
// x 为基本类型
resolve(x)
}
} catch (e) {
reject(e)
}
}
}