1.1. Promise 是什么?
1.1.1. 理解
- 抽象表达: Promise 是 JS 中进行异步编程的新的解决方案(旧的是谁?)
- 具体表达:
- 从语法上来说: Promise 是一个构造函数
- 从功能上来说: promise 对象用来封装一个异步操作并可以获取其结果
1.1.2 promise 的状态
- 待定(pending) :初始状态,既没有被兑现,也没有被拒绝。
- 已兑现(fulfilled) :意味着操作成功完成。
- 已拒绝(rejected) :意味着操作失败。
1.1.3 状态的改变
- pending 变为 resolved
- pending 变为 rejected
说明:
只有这 2 种状态变化, 且一个 promise 对象只能改变一次
无论变为成功还是失败, 都会有一个结果数据
成功的结果数据一般称为 vlaue, 失败的结果数据一般称为 reason
1.1.4 promise 的基本流程
1.1.5 promise 的基本使用
我们使用new Promise构造器创建一个promise实例对象,并且必须要传入executor函数作为参数。
let p = new Promise((resolve, reject) => {});
setTimeout(console.log, 0, p); // Promise <pending>
执行器函数主要有两项职责:初始化期约的异步行为和控制状态的最终转换。在执行器函数中调用 resolve()会把状态切换为兑现,调用 reject()会把状态切换为拒绝,拒绝后需要捕获错误。
let p1 = new Promise((resolve, reject) => resolve());
setTimeout(console.log, 0, p1); // Promise <resolved>
let p2 = new Promise((resolve, reject) => reject());
setTimeout(console.log, 0, p2); // Promise <rejected>
// Uncaught error (in promise)
promise实例通过then方法处理异步程序。then()方法接受最多两个参数:onResolved 处理程序和 onRejected 处理程序。这两个参数都是可选的,如果提供的话,则会在期约分别进入“兑现”和“拒绝”状态时执行。
const p = new Promise((resolve, reject) => {
setTimeout(() => {
const time = Date.now() // 如果当前时间是偶数就代表成功, 否则代表失败
// 如果成功了, 调用resolve(value)
if (time % 2 == 0) {
resolve('成功的数据, time=' + time)
} else {
// 如果失败了, 调用reject(reason)
reject('失败的数据, time=' + time)
}
}, 1000)
})
p.then(
value => { // 接收得到成功的value数据 onResolved
console.log('成功的回调', value)
},
reason => {// 接收得到失败的reason数据 onRejected
console.log('失败的回调', reason)
}
)
1.2 为什么要使用promise
1.2.1 早期的异步编程
在早期的 JavaScript 中,只支持定义回调函数来表明异步操作完成。串联多个异步操作是一个常见的问题,通常需要深度嵌套的回调函数(俗称“回调地狱”)来解决。
function double(value, success, failure) {
setTimeout(() => {
try {
if (typeof value !== 'number') {
throw 'Must provide number as first argument';
}
success(2 * value);
} catch (e) {
failure(e);
}
}, 1000);
}
const successCallback = (x) => console.log(`Success: ${x}`);
const failureCallback = (e) => console.log(`Failure: ${e}`);
double(3, successCallback, failureCallback);
double('b', successCallback, failureCallback);
这种方式必须在初始化异步操作时定义回调。如果异步返值又依赖另一个异步返回值,那么回调的情况还会进一步变复杂。
1.2.2 promise处理结果回调
promise可以在异步执行之后指定回调函数,并且支持链式调用。
let p = new Promise((resolve, reject) => {
resolve();
});
p.then(() => console.log('second'))
.then(() => console.log('third'))
.then(() => console.log('fourth'));
// first
// second
// third
// fourth
1.2.3 async/await 终极解决异步方案
虽然promise解决了回调地狱的问题,但本质上还是使用了回调函数的方式。异步函数,也称为“async/await”(语法关键字),是 ES6 期约模式在 ECMAScript 函数中的应用。async/await 是 ES8 规范新增的。这个特性从行为和语法上都增强了 JavaScript,让以同步方式写的代码能够异步执行。
async function request() {
try {
const result = await doSomething()
const newResult = await doSomethingElse(result)
const finalResult = await doThirdThing(newResult)
} catch (error) {
failureCallback(error)
}
}
以上伪代码表示,依次执行 doSomething,doSomethingElse,doThirdThing,并得到最终结果finalResult。 如果发生错误,会在failureCallback中捕获处理。
1.3 Promise的使用
1.3.1 Promise 构造函数
Promise(excutor)
const excutor = (resolve, reject) => {}
- excutor 函数:执行器 (resolve, reject) => {}
- resolve 函数: 内部定义成功时我们调用的函数 value => {}
- reject 函数: 内部定义失败时我们调用的函数 reason => {}
- excutor 会在 Promise 内部立即同步回调,异步操作在执行器中执行
1.3.2 Promise.prototype.then
promise.then(onResolved, onRejected)
- onResolved 函数: 成功的回调函数 (value) => {}
- onRejected 函数: 失败的回调函数 (reason) => {}
- 指定用于得到成功 value 的成功回调和用于得到失败 reason 的失败回调
- then 方法返回一个新的promise实例对象
then方法指定回调函数是同步的,但是then方法中指定的onResolved 函数和onRejected 函数是异步执行。
new Promise((resolve, reject) => {
resolve(1) // 先改变的状态(同时指定数据)
}).then(// 后指定回调函数, 异步执行回调函数
value => {console.log('value', value)},
reason => {console.log('reason', reason)}
)
console.log('先输出')
1.3.3 Promise.prototype.catch
promise.catch(onRejected)
- onRejected 函数: 失败的回调函数 (reason) => {}
- catch方法相当于then方法的语法糖:then(undefined,onRejected)
Promise.resolve
静态方法。当传入的参数非promise对象时,会返回一个包装当前值的resolved状态的promise对象;当传入的参数是一个promise对象是,返回的是跟当前保持一致状态的promise对象。
let p1 = Promise.resolve(1)
p1.then(value => console.log(value)) // 返回的是resolved状态的promise对象,可以在onResolved回调中接受,此时value 为1
let p2 = new Promise((resovle, reject) => reject('err'))
let p3 = Promise.resolve(p2)
p3.catch(reason => console.log(reason)) // err
let p4 = new Promise((resovle, reject) => resolve('sucess'))
let p5 = Promise.resolve(p4)
p4.then(value => console.log(value)) // sucess
Promise.reject
静态方法。返回一个rejected状态的promise对象。
// 传入的是一个非promise值
let p1 = Promise.reject(1)
p1.catch(value => console.log('p1',value)) // 返回的是rejected状态的promise对象,可以在 onRejected回调/catch方法 中接受,此时value 为1
// 传入一个 rejected 状态的 promise对象
let p2 = new Promise((resolve, reject) => {reject('p2-err')})
p2.catch(reason => console.log('p2', reason)) // p2-err
let p3 = Promise.reject(p2)
// 此时的reason就是p2,reason 通用可以在 onRejected回调/catch方法 捕获错误
p3.catch(reason => console.log('p3',reason))
// 传入一个 resolved 状态的 promise对象
let p4 = new Promise((resolve, reject) => resolve('sucess'))
let p5 = Promise.reject(p4)
// 此时的reason就是p4,reason 通用可以在 onResolved回调中接受值
p5.catch(reason => console.log('p5',reason.then(val => console.log('val',val)))) // sucess
Promise.all
静态方法。接受一个包含多个promise对象的数组,返回一个新的promise;只有所有的promise都成功才成功, 只要有一个失败了就直接失败;成功的时候,在onResolved回调中接受的values是一个数组,且数组中按传入的promise顺序一一对应;失败的时候,原因是第一个失败的原因。
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1)
}, 100);
})
const p2 = Promise.resolve(2)
const p3 = Promise.reject(3)
const p4 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(4)
},100)
})
当成功时,返回的values数组中的value跟传入的promise对象顺序一一对应
// p2的状态改变要比p1的状态改变早
const pAll = Promise.all([p1, p2])
pAll.then(values => console.log(values)) // [1,2]
当失败时,失败的原因时第一个失败的原因
const pAll = Promise.all([p1, p2, p4, p3])
pAll.catch(reason => console.log(reason)) // 3,因为p3是最早失败的
Promise.race
静态方法。接受一个包含多个promise对象的数组,返回的结果是由首个状态改变的promsie对象决定。
当首个promsie对象的状态改变是成功时,返回的是当前promise对象成功的value
const pRace = Promise.race([p1, p2, p3])
pRace.then(
value => {
console.log(value)
},
reason => {
console.log(reason)
}
)
// 最终输出2
当首个promise对象的状态改变是失败时,返回的是当前promise对象失败的reason
const pRace = Promise.race([p3, p2, p1])
pRace.then(
value => {
console.log(value)
},
reason => {
console.log(reason)
}
)
// 最终输出3
1.4 promise的几个关键问题
1.4.1 如何改变promise的状态?
resolve(value): 如果当前是pendding就会变为resolved
const p = new Promise((resolve, reject) => resolve('sucess'))
p.then(value => console.log(value)) // 输出 sucess
reject(reason): 如果当前是pendding就会变为rejected
const p = new Promise((resolve, reject) => reject('reason'))
p.catch(reason => console.log(reason)) // 输出 reason
抛出异常: 如果当前是pendding就会变为rejected
const p1 = new Promise((resolve, reject) => throw Error('reason'))
p1.catch(reason => console.log(reason)) // 输出 reason
// 可以throw 任意值,不一定是Error
const p2 = new Promise((resolve, reject) => throw ('reason'))
p2.catch(reason => console.log(reason)) // 输出 reason
1.4.2 一个promise指定多个成功/失败回调函数, 都会调用吗?
当指定多个成功的回调函数
const p = new Promise((resolve, reject) => resolve('sucess'))
p.then(value => console.log('val1:',value)) // 输出 val1:sucess
p.then(value => console.log('val2:',value)) // 输出 val2:sucess
当指定多个失败的回调函数
const p = new Promise((resolve, reject) => reject('reason'))
p.catch(reason => console.log('reason1:',reason)) // 输出 reason1:reason
p.catch(reason => console.log('reason2:',reason)) // 输出 reason2:reason
因此,当promise改变为对应状态时,指定的对应回调函数都会调用
1.4.3 改变promise状态和指定回调函数谁先谁后?
一般情况: 先指定回调函数, 后改变的状态
new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1) // 后改变的状态(同时指定数据), 异步执行回调函数
}, 1000);
}).then(// 先指定回调函数, 保存当前指定的回调函数
value => {},
reason => {console.log('reason', reason)}
)
我们也可以先改变状态,后指定回调函数
new Promise((resolve, reject) => {
resolve(1) // 先改变的状态(同时指定数据)
}).then(// 后指定回调函数, 异步执行回调函数
value => {console.log('value', value)},
reason => {console.log('reason', reason)}
)
1.4.4 promise.then()返回的新 promise 的结果状态由什么决定?
用一句话来概括:由then()指定的回调函数执行的结果决定
当then中执行的onResolved/onRejected 返回一个非promise的任意值,新promise变为resolved, value为返回的值
当then中执行onResolved回调,并且返回一个非promise的任意值
new Promise((resolve, reject) => {
resolve(1)
}).then(
value => {
return 2
},
reason => {
return 2
}
).then(
value => {
console.log('onResolved()', value) // 新的promise是resolved状态,输出:onResolved() 2
},
reason => {
console.log('onRejected()', reason)
}
)
当then中执行onRejected回调,并且返回一个非promise的任意值
new Promise((resolve, reject) => {
reject(1)
}).then(
value => {
return 2
},
reason => {
return 2
}
).then(
value => {
console.log('onResolved()', value) //新的promise是resolved状态, 输出:onResolved() 2
},
reason => {
console.log('onRejected()', reason)
}
)
当then中执行的onResolved/onRejected 返回的是另一个新promise, 此promise的结果就会成为新promise的结果
当then中执行onResolved回调,并且返回一个promise
new Promise((resolve, reject) => {
resolve(1)
}).then(
value => {
return Promise.resolve(3)
}
).then(
value => {
console.log('onResolved()', value) // 新的promise是成功状态,输出onResolved() 3
},
reason => {
console.log('onRejected()', reason)
}
)
new Promise((resolve, reject) => {
resolve(1)
}).then(
value => {
return Promise.reject(3)
}
).then(
value => {
console.log('onResolved()', value)
},
reason => {
console.log('onRejected()', reason)// 新的promise是失败状态,onRejected() 3
}
)
当then中执行onRejected回调,并且返回一个promise
new Promise((resolve, reject) => {
reject(1)
}).then(
undefined,
reason => {
return Promise.resolve(3)
}
).then(
value => {
console.log('onResolved()', value) // 新的promise是成功状态,输出onResolved() 3
},
reason => {
console.log('onRejected()', reason)
}
)
new Promise((resolve, reject) => {
reject(1)
}).then(
undefined,
reason => {
return Promise.reject(3)
}
).then(
value => {
console.log('onResolved()', value)
},
reason => {
console.log('onRejected()', reason)// 新的promise是失败状态,输出onRejected() 3
}
)
当then中执行的onResolved/onRejected中抛出异常, 新promise变为rejected, reason为抛出的异常
new Promise((resolve, reject) => {
resolve(1)
}).then(
value => {
throw 3
}
).then(
value => {
console.log('onResolved()', value)
},
reason => {
console.log('onRejected()', reason)// 新的promise是失败状态,输出onRejected() 3
}
)
new Promise((resolve, reject) => {
reject(1)
}).then(
undefined,
reason => {
throw 3
}
).then(
value => {
console.log('onResolved()', value)
},
reason => {
console.log('onRejected()', reason)// 新的promise是失败状态,输出onRejected() 3
}
)
1.4.5 promise 如何串联多个操作任务
promise的then()返回一个新的promise, 可以开成then()的链式调用,通过then的链式调用串连多个同步/异步任务
new Promise((resolve, reject) => {
setTimeout(() => {
console.log("执行任务1(异步)")
resolve(1)
}, 1000);
}).then(
value => {
console.log('任务1的结果: ', value) // 输出:1
console.log('执行任务2(同步)')
return 2
}
).then(
value => {
console.log('任务2的结果:', value) // 输出: 2
return new Promise((resolve, reject) => {
// 启动任务3(异步)
setTimeout(() => {
console.log('执行任务3(异步))')
Promise.resolve(3)
}, 1000);
})
}
).then(
value => {
console.log('任务3的结果: ', value) // 输出:3
}
)
如果then中回调函数返回的是一个异步任务,需要包装成一个新的promise返回。
1.4.6 promise 异常穿透
当使用promise的then链式调用时,可以在最后指定失败的回调
new Promise((resolve, reject) => {
reject(1)
}).then(
value => {
console.log('onResolved1()', value)
return 2
}
).then(
value => {
console.log('onResolved2()', value)
return 3
}
).then(
value => {
console.log('onResolved3()', value)
}
).catch(reason => {
console.log('onRejected1()', reason) // 最终输出 onRejected1() 1
})
上述代码中,在前面then的链式调用中没有指定失败的回调,所以在最后catch中捕获错误。前面没有指定失败的回调,相当于在失败的回调返回一个失败的promise并且理由为,首次失败时的reason。
new Promise((resolve, reject) => {
reject(1)
}).then(
value => {
console.log('onResolved1()', value)
return 2
},
reason => {throw reason} // 向后抛出reason
).then(
value => {
console.log('onResolved2()', value)
return 3
},
reason => {throw reason} // 向后抛出reason
).then(
value => {
console.log('onResolved3()', value)
},
reason => Promise.reject(reason) // 向后返回一个状态为rejected的promise
).catch(reason => {
console.log('onRejected1()', reason) // 最终输出 onRejected1() 1
})
1.4.6 中断promise链
上述代码中,catch捕获后依然返回了一个成功的promise,并且value为undefined
new Promise((resolve, reject) => {
reject(1)
}).then(
value => {
console.log('onResolved1()', value)
return 2
},
reason => {throw reason} // 向后抛出reason
).then(
value => {
console.log('onResolved2()', value)
return 3
},
reason => {throw reason} // 向后抛出reason
).then(
value => {
console.log('onResolved3()', value)
},
reason => Promise.reject(reason) // 向后返回一个状态为rejected的promise
).catch(reason => {
console.log('onRejected1()', reason)
}).then(
value => console.log(value) // 输出:undefined
)
如何中断后续的then方法中的回调执行呢?换一句话说,then方法中的回调在什么时候执行?
then方法中指定的onResolve和onReject 回调函数分别是在promise状态resolved和rejected时执行,那么也就是说当promise的状态为pendding时,两个回调是不执行的。因此,我们可以通过返回一个pending状态的promise来中断promise的链式调用
new Promise((resolve, reject) => {
reject(1)
}).then(
value => {
console.log('onResolved1()', value)
return 2
},
reason => {throw reason} // 向后抛出reason
).then(
value => {
console.log('onResolved2()', value)
return 3
},
reason => {throw reason} // 向后抛出reason
).then(
value => {
console.log('onResolved3()', value)
},
reason => Promise.reject(reason) // 向后返回一个状态为rejected的promise
).catch(reason => {
console.log('onRejected1()', reason)
return new Promise((resolve,reject)=>{})
}).then(
value => console.log(value) , // 没有执行
reason => console.log(reason) // 没有执行
)