Promise
-
resolve()
&reject()
let p1 = new Promise( (resolve, reject) => { resolve() reject() // 无效,状态不可撤销,走了其第一个就不会走第二个 } )
-
Promise.resolve()
// p1, p2 等价 let p1 = new Promise( (resolve, reject) => resolve() ) let p2 = Promise.resolve()
// 只接受第一个参数 console.log( Promise.resolve() ) // Promise {<resolved>: undefined} console.log( Promise.resolve(3) ) // Promise {<resolved>: 3} console.log( Promise.resolve(3, 4, 5) ) // Promise {<resolved>: 3}
-
Promise.reject()
// Promise reject 的错误并没有抛到执行同步代码的线程里,而是通过浏览器异步消息队列来处理的。因此,try/catch块并不能捕获该错误。 try { throw new Error('foo'); } catch(e) { console.log(e); // Error: foo } try { Promise.reject(new Error('bar')); } catch(e) { console.log(e); } // Uncaught (in promise) Error: bar
-
Promise.prototype.then()
// .then( param_1, param_2 ) // param_1 { function } 执行 Promise 的 resolve // param_2 { function } 执行 Promise 的 reject let p_to_res = new Promise( (res, rej) => setTimeout(res, 3000) ) let p_to_rej = new Promise( (res, rej) => setTimeout(rej, 3000) ) let onRes = (id) => console.log(id, 'res') let onRej = (id) => console.log(id, 'rej') p_to_res.then( () => onRes('p_to_res'), () => onRej('p_to_res') ) p_to_rej.then( () => onRes('p_to_rej'), () => onRej('p_to_rej') ) // (3s后) // p_to_res res // p_to_rej rej
// 非函数处理程序会被静默忽略,不推荐 p_to_res.then( 'hello' ) // 推荐 p_to_rej.then( null, () => onRej('p_to_rej') )
// .then()方法返回一个新的Promise实例: let p1 = new Promise(() => {}); let p2 = p1.then(); console.log(p2) // Promise {<pending>} console.log(p1 === p2) // false
// 参数传递 let p = new Promise((resolve) => resolve('success!')) p.then((value) => console.log(value)) // success!
-
Promise.prototype.catch()
// .catch( param ) // param { function } 执行 Promise 的 reject // .catch( onRejected ) === .then(null, onRejected) let p = Promise.reject() let onRej = () => console.log('rej') // 下面两种方法等价 p.then( null, onRej ) p.catch( onRej )
// 参数传递 let p = new Promise((resolve, reject) => reject('fail!')) p.catch((value) => console.log(value)) // fail!
-
Promise.prototype.finally()
// .finally( param ) // param { function } 执行 Promise 的 resolve 或 reject let p_to_res = new Promise( (res, rej) => setTimeout(res, 3000) ) let p_to_rej = new Promise( (res, rej) => setTimeout(rej, 3000) ) let onFin = () => console.log('Finally') p_to_res.finally( onFin ) // Finally p_to_rej.finally( onFin ) // Finally
非重入(non-reentrancy) - about 执行顺序
// 即使promise状态立即落定,.then 仍在同步代码之后执行
let p = Promise.resolve()
p.then(() => console.log('run .then'))
console.log('after .then')
// after .then
// run .then
// 即使resolve()之后的console,也会先于 .then 执行
let syncFunc = null
let p = new Promise((resolve) => {
syncFunc = () => {
console.log('1: 调用 resolve')
resolve()
console.log('2: resolve 已执行')
}
})
p.then(() => console.log('4: .then 已执行'))
syncFunc() // 此时 syncFunc 函数的定义已完成
console.log('3: syncFunc 已执行')
// 1: 调用 resolve
// 2: resolve 已执行
// 3: syncFunc 已执行
// 4: .then 已执行
副作用
// 正常情况下,在通过`throw()`关键字抛出错误时,JavaScript会停止执行抛出错误之后的任何指令
throw Error('error')
console.log('log see see') // 这行不会执行
// Uncaught Error Error: error
// 在Promise中,由于 non-reentrancy 特性,不会阻止同步指令执行
Promise.reject( Error('error') )
console.log('log see see')
// log see see
// UnhandledPromiseRejectionWarning: Error: error
Promise
链
// 由于`.then` `.catch` `.finally` 都会返回一个新的`Promise`对象
let func = new Promise((res, rej) => {
console.log('1 done')
res()
})
func
.then(() => console.log('2 done'))
.then(() => console.log('3 done'))
.then(() => console.log('4 done'))
// 1 done
// 2 done
// 3 done
// 4 done
// 执行延时异步动作
let delay_1s_Func = (logText) => new Promise((res, rej) => {
console.log(logText)
setTimeout(res, 1000)
})
delay_1s_Func('p1 done')
.then(() => delay_1s_Func('p2 done'))
.then(() => delay_1s_Func('p3 done'))
.then(() => delay_1s_Func('p4 done'))
// p1 done (1s 后)
// p2 done (2s 后)
// p3 done (3s 后)
// p4 done (4s 后)
Promise.all()
// 待数组中所有 Promise 都 resolve,执行下一步
// 常规用法
let p1 = Promise.all([
Promise.resolve(),
Promise.resolve(),
Promise.resolve()
])
// 默认转换成 Promise.resolve()
let p2 = Promise.all([1, 2])
let p3 = promise.all([])
// 无效语法
let p4 = Promise.all()
// UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'Symbol(Symbol.iterator)' of undefined
// .then 拿到所有 Promise 解决值的数组
// 顺序按原顺序
let p1 = new Promise((res) => {
setTimeout(() => res('1s delay done'), 1000)
})
let p2 = new Promise((res) => {
setTimeout(() => res('2s delay done'), 2000)
})
let p3 = new Promise((res) => {
setTimeout(() => res('3s delay done'), 3000)
})
Promise.race([p1, p2, p3])
.then( (res) => console.log(res) )
// ['1s delay done', '2s delay done', '3s delay done']
// 如果有一项 promise reject,则最终 reject
let p1 = new Promise((res) => {
setTimeout(() => res('1s delay done'), 1000)
})
let p2 = new Promise((res, rej) => {
setTimeout(() => rej('2s delay error'), 2000)
})
let p3 = new Promise((res) => {
setTimeout(() => res('3s delay done'), 3000)
})
Promise.race([p1, p2, p3])
.then( (res) => console.log(res) )
.catch( (err) => console.log(err) )
// 2s delay error
Promise.race()
// 是一组集合中最先解决或拒绝的 Promise 的镜像
// 常规用法
let p1 = Promise.race([
Promise.resolve(),
Promise.resolve(),
Promise.resolve()
])
// 默认转换成 Promise.resolve()
let p2 = Promise.race([1, 2])
let p3 = promise.race([])
// 无效语法
let p4 = Promise.race()
// UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'Symbol(Symbol.iterator)' of undefined
// 不会对解决或拒绝的期约区别对待。无论是解决还是拒绝,只要是第一个落定的Promise
let p1 = new Promise((res) => {
setTimeout(() => res('1s delay done'), 1000)
})
let p2 = new Promise((res, rej) => {
setTimeout(() => rej('2s delay error'), 2000)
})
let p3 = new Promise((res) => {
setTimeout(() => res('3s delay done'), 3000)
})
Promise.race([p1, p2, p3])
.then( (res) => console.log(res) )
.catch( (err) => console.log(err) )
// 1s delay done
Promise.race([p2, p3])
.then( (res) => console.log(res) )
.catch( (err) => console.log(err) )
// 2s delay error
Promise 扩展
ECMAScript规范却未涉及的两个特性:取消 和 进度追踪
Promise取消
第三方库:Bluebird
现有实现基础可以提供一种临时封装 - 取消令牌 CancelToken
class CancelToken {
constructor(cancelFn) {
this.promise = new Promise((resolve, reject) => {
cancelFn(resolve);
});
}
}
// 用法
<button id="start">Start</button>
<button id="cancel">Cancel</button>
<script>
class CancelToken {
constructor(cancelFn) {
this.promise = new Promise((resolve, reject) => {
cancelFn(() => {
setTimeout(console.log, 0, "delay cancelled");
resolve();
});
});
}
}
const startButton = document.querySelector('#start');
const cancelButton = document.querySelector('#cancel');
function cancellableDelayedResolve(delay) {
setTimeout(console.log, 0, "set delay");
return new Promise((resolve, reject) => {
const id = setTimeout((() => {
setTimeout(console.log, 0, "delayed resolve");
resolve();
}), delay);
const cancelToken = new CancelToken((cancelCallback) =>
cancelButton.addEventListener("click", cancelCallback));
cancelToken.promise.then(() => clearTimeout(id));
});
}
startButton.addEventListener("click", () => cancellableDelayedResolve(1000));
</script>
// vue3 版本
<script setup>
class CancelToken {
constructor(cancelFn) {
this.promise = new Promise((resolve) => {
cancelFn(() => resolve())
})
}
}
const start = () => delayDoSomething(1000)
const end = () => _end()
let _end = () => {}
const delayDoSomething = ( delay ) => {
console.log('set delay')
return new Promise((resolve, reject) => {
const id = setTimeout(() => {
console.log('delayed resolve')
resolve()
}, delay);
const cancleToken = new CancelToken( (cancelCallback) => _end = cancelCallback )
cancleToken.promise.then(() => clearTimeout(id))
})
}
</script>
<template>
<div>
<button @click="start">start</button>
<button @click="end">end</button>
</div>
</template>