共计 8687 个字符,预计需要花费 22 分钟才能阅读完成。
一、Promise 是什么
在 JavaScript 中,Promise 是一种解决异步操作的形式。Promise 能够让咱们更加优雅地解决异步操作,而不须要应用回调函数嵌套。
● Promise 是一门新的技术(ES6 标准),是 JS 中进行异步编程的新解决方案(旧计划是单纯应用回调函数,容易造成回调天堂)
● 常见异步操作:①fs 文件操作 ②数据库操作 ③Ajax ④定时器
● 具体表白:
- 从语法上看:Promise 是一个构造函数 (本人身上有 all、reject、resolve 这几个办法,原型上有 then、catch 等办法)
- 从性能上看:promise 对象用来封装一个异步操作并能够获取其胜利 / 失败的后果值
二、创立 Promise
创立一个 Promise 非常简单,只须要应用 Promise
构造函数即可。Promise
构造函数承受一个函数作为参数,这个函数叫做执行器函数(executor function)。执行器函数有两个参数:resolve
和 reject
。当异步操作胜利时,调用 resolve
,并传递后果值作为参数;当异步操作失败时,调用 reject
,并传递谬误对象作为参数。
const promise = new Promise(function(resolve, reject) { | |
// ... some code | |
if (/* 异步操作胜利 */){resolve(value); | |
} else {reject(reason); | |
} | |
}); |
三、应用 Promise
应用 Promise 有两种形式:then
办法和 catch
办法。
1.then 办法
then
办法用于注册 Promise 胜利时的回调函数。当 Promise 胜利时,then
办法会被调用,并传递 Promise 胜利时的后果值作为参数。
promise.then((result) => {// 解决 Promise 胜利时的后果值});
2.catch 办法
catch
办法用于注册 Promise 失败时的回调函数。当 Promise 失败时,catch
办法会被调用,并传递 Promise 失败时的谬误对象作为参数。
promise.catch((error) => {// 解决 Promise 失败时的谬误对象});
四、Promise.resolve 和 Promise.reject
1.Promise.resolve 办法
在 JavaScript 中,Promise.resolve 是一个办法,它返回一个 Promise 对象,该对象以给定的值解析。如果该值自身是一个 Promise,则返回该 Promise;否则,返回一个以该值解析的新 Promise 对象。它通常用于将非 Promise 值转换为 Promise 对象,以便在 then 办法中应用。
Promise.resolve(value):
value:将被 Promise 对象解析的参数,也能够是一个胜利或失败的 Promise 对象
返回一个带着给定值解析过的 Promise 对象,如果参数自身就是一个 Promise 对象,则间接返回这个 Promise 对象
- 1. 如果传入的参数为 非 Promise 类型的对象, 则返回的后果为胜利 promise 对象
let p1 = Promise.resolve(521); | |
console.log(p1); // Promise {<fulfilled>: 521} |
- 2. 如果传入的参数为 Promise 对象, 则参数的后果决定了 resolve 的后果
let p2 = Promise.resolve(new Promise((resolve, reject) => {// resolve('OK'); // 胜利的 Promise | |
reject('Error'); | |
})); | |
console.log(p2); | |
p2.catch(reason => {console.log(reason); | |
}) |
2.Promise.reject 办法
Promise.reject(reason):
reason:失败的起因
阐明:返回一个失败的 promise 对象
let p = Promise.reject(521); | |
let p2 = Promise.reject('iloveyou'); | |
let p3 = Promise.reject(new Promise((resolve, reject) => {resolve('OK'); | |
})); | |
console.log(p); | |
console.log(p2); | |
console.log(p3); //reject 返回的 promise 调用的是 resolve |
● Promise.resolve()/Promise.reject() 办法就是一个语法糖,用来疾速失去 Promise 对象
五、Promise 的状态和状态转换
Promise 有三种状态:
pending
(进行中)、fulfilled
(已胜利)和 rejected
(已失败)。Promise 的状态只能从 pending
转换为 fulfilled
或 rejected
,一旦状态转换实现,就不能再次转换。
上面是一个简略的例子,展现了 Promise 的状态和状态转换。
const promise = new Promise((resolve, reject) => {setTimeout(() => {resolve('result'); | |
}, 1000); | |
}); | |
console.log(promise); // Promise {<pending>} | |
promise.then((result) => {console.log(result); // result | |
console.log(promise); // Promise {<fulfilled>:'result'} | |
}); | |
console.log(promise); // Promise {<pending>} |
在下面的例子中,Promise 的初始状态为 pending
,因为异步操作须要 1 秒钟能力实现。当异步操作实现并胜利时,Promise 状态转换为 fulfilled
。在 then
办法中,咱们能够拜访 Promise 的后果值。最初,咱们再次打印 Promise 对象,能够看到它的状态曾经变为 fulfilled
。
如果异步操作失败,咱们能够调用 reject
办法,将 Promise 状态转换为 rejected
。
const promise = new Promise((resolve, reject) => {setTimeout(() => {reject(new Error('error')); | |
}, 1000); | |
}); | |
promise.catch((error) => {console.log(error); // Error: error | |
console.log(promise); // Promise {<rejected>: Error: error} | |
}); |
在下面的例子中,异步操作失败,并且 Promise 状态转换为 rejected
。在 catch
办法中,咱们能够拜访 Promise 的谬误对象。
六、Promise 的几个关键问题
💡 a、如何扭转 promise 的状态?
1.resolve(value):如果以后是 pending 就会变为 resolved
2.reject(reason):如果以后是 pending 就会变为 rejected
3. 抛出异样:如果以后是 pending 就会变为 rejected
💡 b、一个 promise 指定多个胜利 / 失败回调函数,都会调用吗?
当 promise 扭转为对应状态时都会调用
const p = new Promise((resolve, reject) => {//resolve(1) | |
reject(2) | |
}) | |
p.then(value => {}, | |
reason => {console.log('reason',reason)} | |
) | |
p.then(value => {}, | |
reason => {console.log('reason2',reason)} | |
) | |
// reason 2 | |
// reason2 2 |
💡 c、promise.then() 返回的新 promise 的后果状态由什么决定?
(1)简略表白:由 then() 指定的回调函数执行的后果决定
(2)具体表白:
① 如果抛出异样,新 promise 变为 rejected,reason 为抛出的异样
② 如果返回的是非 promise 的任意值,新 promise 变为 resolved,value 为返回的值
③ 如果返回的是另一个新 promise,此 promise 的后果就会成为新 promise 的后果
💡 d、promise 如何串联多个操作工作?
(1)promise 的 then() 返回一个新的 promise,能够并成 then() 的链式调用 | |
(2)通过 then 的链式调用串联多个同步 / 异步工作 | |
new Promise((resolve, reject) => {setTimeout(() => {console.log('执行工作 1(异步)') | |
resolve(1) | |
}, 1000) | |
}).then( | |
value => {console.log('工作 1 的后果', value) | |
console.log('执行工作 2(同步)') | |
return 2 // 同步工作间接 return 返回后果 | |
} | |
).then( | |
value => {console.log('工作 2 的后果', value) | |
return new Promise((resolve, reject) => { // 异步工作须要包裹在 Promise 对象中 | |
setTimeout(() => {console.log('执行工作 3(异步)') | |
resolve(3) | |
}, 1000) | |
}) | |
} | |
).then( | |
value => {console.log('工作 3 的后果', value) | |
} | |
) | |
// 执行工作 1(异步) | |
// 工作 1 的后果 1 | |
// 执行工作 2(同步) | |
// 工作 2 的后果 2 | |
// 执行工作 3(异步) | |
// 工作 3 的后果 3 |
💡 e、Promise 异样穿透(传透)?
(1)当应用 promise 的 then 链式调用时,能够在最初指定失败的回调
(2)后面任何操作出了异样,都会传到最初失败的回调中解决
new Promise((resolve, reject) => {//resolve(1) | |
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 |
失败的后果是一层一层解决下来的,最初传递到 catch 中
七、Promise.all 和 Promise.race
除了根底的 Promise,ES6 还提供了两个静态方法:Promise.all
和 Promise.race
。这两个办法能够让咱们更加不便地解决多个 Promise 实例。
1.Promise.all
Promise.all
办法用于将多个 Promise 实例包装成一个新的 Promise 实例。当所有 Promise 实例都胜利时,新的 Promise 实例才会胜利;当任意一个 Promise 实例失败时,新的 Promise 实例就会失败。
const promise1 = Promise.resolve(1); | |
const promise2 = Promise.resolve(2); | |
Promise.all([promise1, promise2]).then((results) => {console.log(results); // [1, 2] | |
}); |
在下面的例子中,Promise.all
办法承受一个 Promise 实例数组作为参数,返回一个新的 Promise 实例。当所有 Promise 实例都胜利时,新的 Promise 实例会胜利,并传递所有 Promise 实例的后果值数组作为参数。
2.Promise.race
Promise.race
办法用于将多个 Promise 实例包装成一个新的 Promise 实例。当其中申请最快的那个 Promise 实例胜利或失败时,新的 Promise 实例就会胜利或失败。
const promise1 = new Promise((resolve) => {setTimeout(() => {resolve(1); | |
}, 1000); | |
}); | |
const promise2 = new Promise((resolve) => {setTimeout(() => {resolve(2); | |
}, 2000); | |
}); | |
Promise.race([promise1, promise2]).then((result) => {console.log(result); // 1 | |
}); |
在下面的例子中,Promise.race
办法承受一个 Promise 实例数组作为参数,返回一个新的 Promise 实例。当其中申请最快一个 Promise 实例胜利或失败时,新的 Promise 实例就会胜利或失败,并传递第一个胜利或失败的 Promise 实例的后果值或谬误对象作为参数。
艰深的讲,就是说你可能传给我了好几个异步,我都会去申请,然而只有有一个率先实现申请我就把他的后果 (胜利 / 失败) 返回给你。至于剩下的申请,我只帮你申请,他是对是错我就不论了,我只关注最快的那个。
八、Promise 利用场景
1. 防止回调天堂
Promise 能够让咱们更加优雅地解决异步操作,防止回调天堂。在应用 Promise 时,咱们应该尽量避免应用回调函数嵌套,而是应用 Promise 链式调用的形式。
asyncFunc1() | |
.then((result1) => {return asyncFunc2(result1); | |
}) | |
.then((result2) => {return asyncFunc3(result2); | |
}) | |
.then((result3) => {console.log(result3); | |
}) | |
.catch((error) => {console.log(error); | |
}); |
2. 处理错误
在应用 Promise 时,咱们应该尽可能地处理错误。应用 catch
办法能够解决 Promise 的谬误,避免出现未解决的谬误。
asyncFunc() | |
.then((result) => {console.log(result); | |
}) | |
.catch((error) => {console.log(error); | |
}); |
3. 返回 Promise
在编写函数时,如果函数外部有异步操作,咱们应该返回一个 Promise 实例,以便内部代码能够应用 Promise 的形式解决异步操作。
function asyncFunc() {return new Promise((resolve, reject) => { | |
// 异步操作 | |
// 胜利时调用 resolve(result) | |
// 失败时调用 reject(error) | |
}); | |
} |
九、手写 Promise 实现原理
class MyPromise {constructor(executor) { | |
this.state = 'pending'; | |
this.value = undefined; | |
this.reason = undefined; | |
this.onResolvedCallbacks = []; | |
this.onRejectedCallbacks = []; | |
const resolve = (value) => {if (this.state === 'pending') { | |
this.state = 'fulfilled'; | |
this.value = value; | |
this.onResolvedCallbacks.forEach((fn) => fn()); | |
} | |
}; | |
const reject = (reason) => {if (this.state === 'pending') { | |
this.state = 'rejected'; | |
this.reason = reason; | |
this.onRejectedCallbacks.forEach((fn) => fn()); | |
} | |
}; | |
try {executor(resolve, reject); | |
} catch (error) {reject(error); | |
} | |
} | |
then(onFulfilled, onRejected) {onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (value) => value; | |
onRejected = typeof onRejected === 'function' ? onRejected : (reason) => {throw reason;}; | |
const promise2 = new MyPromise((resolve, reject) => {if (this.state === 'fulfilled') {setTimeout(() => { | |
try {const x = onFulfilled(this.value); | |
this.resolvePromise(promise2, x, resolve, reject); | |
} catch (error) {reject(error); | |
} | |
}); | |
} | |
if (this.state === 'rejected') {setTimeout(() => { | |
try {const x = onRejected(this.reason); | |
this.resolvePromise(promise2, x, resolve, reject); | |
} catch (error) {reject(error); | |
} | |
}); | |
} | |
if (this.state === 'pending') {this.onResolvedCallbacks.push(() => {setTimeout(() => { | |
try {const x = onFulfilled(this.value); | |
this.resolvePromise(promise2, x, resolve, reject); | |
} catch (error) {reject(error); | |
} | |
}); | |
}); | |
this.onRejectedCallbacks.push(() => {setTimeout(() => { | |
try {const x = onRejected(this.reason); | |
this.resolvePromise(promise2, x, resolve, reject); | |
} catch (error) {reject(error); | |
} | |
}); | |
}); | |
} | |
}); | |
return promise2; | |
} | |
resolvePromise(promise2, x, resolve, reject) {if (promise2 === x) {return reject(new TypeError('Chaining cycle detected')); | |
} | |
let called = false; | |
if (x !== null && (typeof x === 'object' || typeof x === 'function')) { | |
try { | |
const then = x.then; | |
if (typeof then === 'function') {then.call(x, (y) => {if (called) return; | |
called = true; | |
this.resolvePromise(promise2, y, resolve, reject); | |
}, (r) => {if (called) return; | |
called = true; | |
reject(r); | |
}); | |
} else {resolve(x); | |
} | |
} catch (error) {if (called) return; | |
called = true; | |
reject(error); | |
} | |
} else {resolve(x); | |
} | |
} | |
catch(onRejected) {return this.then(null, onRejected); | |
} | |
static resolve(value) {return new MyPromise((resolve) => {resolve(value); | |
}); | |
} | |
static reject(reason) {return new MyPromise((_, reject) => {reject(reason); | |
}); | |
} | |
static all(promises) {return new MyPromise((resolve, reject) => {const results = []; | |
let count = 0; | |
for (let i = 0; i < promises.length; i++) {promises[i].then((result) => {results[i] = result; | |
count++; | |
if (count === promises.length) {resolve(results); | |
} | |
}, reject); | |
} | |
}); | |
} | |
static race(promises) {return new MyPromise((resolve, reject) => {for (let i = 0; i < promises.length; i++) {promises[i].then(resolve, reject); | |
} | |
}); | |
} | |
} |
在下面的代码中,咱们创立了一个 MyPromise
类,它蕴含了 Promise 的根本实现。咱们实现了 Promise 的基本功能,包含状态的扭转、then
办法和 catch
办法的实现、Promise 的链式调用、错误处理和静态方法 resolve
、reject
、all
和 race
的实现。
十、总结
Promise 是一种解决异步操作的形式,它能够让咱们更加优雅地解决异步操作。在应用 Promise 时,咱们应该遵循一些最佳实际,包含防止回调天堂、处理错误和返回 Promise。心愿这篇文章可能帮忙你更好地应用 Promise。