@Dale-Lin
2021-02-24T14:53:29.000000Z
字数 6201
阅读 1023
深入理解ES6
Promise 对象用于表示一个异步操作的完成与否,以及返回的值:
let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve('foo');}, 1000);
});
promise.then((value) => {console.log(value);}); //log 'foo' after 1s.
new Promise((resolve, reject) => {/* executor */})
推荐使用箭头函数传入 (resolve, reject)。
executor 在 Promise 构造函数执行时立即调用,resolve、reject 方法将 Promise 对象的状态 pending 改为 fulfilled 或 rejected。
若在 executor 中抛出一个错误,Promise 对象的状态改为 rejected。
resolve() 方法的参数被用于 .then() 第一个参数接收。
reject() 方法的参数被用于 .then() 第二个参数接收。
executor 函数体内的代码是同步立即执行的。
Promise.prototype.then(onFulfilled(value), onRejected(reason))
接受两个处理参数,对应 Promise 对象的最终状态回调处理。返回一个新 Promise 对象,对于处理函数的返回值:
var a = new Promise((resolve, reject) => {setTimeout(() => {resolve('66666'); console.log(b)}, 1000)});
var b = a.then((val) => a);
console.log(b);
// Promise {<pending>}
// 1s 后输出:
// Promise {<pending>},此时还在 a 修改状态的函数内;
// 说明 b 是新的 promise 对象,这时还没同步;
// 再打印:
// Promise {<resolved>: "66666"},已经同步
a === b;
// false
Promise.prototype.catch(onRejected(reason))
专门处理 rejected 的 Promise 对象,也可用来捕捉错误。返回一个新 Promise 对象,以处理函数的返回值来 reslove。
Promise.prototype.finally(onFinally)
无论当前 Promise 对象状态,调用处理函数。
.then() 和 .catch() 方法是异步的。
Promise.resolve(value)
当不确定一个对象是否为 Promise 对象时,可使用
Promise.resolve()
处理。
不要调用 resolve() 自己的 thenable 对象!会导致无限循环!
var a = new Promise((resolve, reject) => {setTimeout(() => {resolve('66666'); console.log(b)}, 1000)});
var b = Promise.resolve(a);
console.log(b)
// Promise {<pending>}
// 1s 后输出
// Promise {<resolved>: "66666"}
a === b
// true
Promise.reject(reason)
Promise.all(iterable)
返回一个 Promise 对象,当 iterable 内所有 Promise 对象都是 resolved 状态,则 resolved,返回包含对应各 resolved 的 value 的数组;否则 rejected,返回第一个 rejected 的错误信息。
Promise.race(iterable)
对于 Promise,有以下保证:
可以使用 Promise chain 来连续执行两个以上异步操作:
const promise2 = promise1.then(successCallback, failureCallback);
有效避免回调地狱:
doSomething().then(function(result) {
return doSomethingElse(result);
})
.then(function(newResult) {
return doThirdThing(newResult);
})
.then(function(finalResult) {
console.log('Got the final result: ' + finalResult;
return 'next arg.');
})
.catch(failureCallback);
.then() 的参数是可选的, .catch(failureCallback) 方法是 .then(null, failureCallback) 的简写。
建议使用箭头函数:
doSomething()
.then(result => doSomethingElse(result))
.then(newResult => doThirdThing(newResult))
.then(finalResult => {
console.log(`Final result: ${finalResult}`);
})
.catch(failureCallback);
返回值很重要,否则回调无法获得前一个 Promise 的结果。
.catch() 的后续链
在失败后还可以继续链式调用,使用 .catch() 实现:
new Promise((resolve, reject) => {
console.log('Initial');
resolve();
})
.then(() => {
throw new Error('Something failed');
console.log('Do this');
})
.catch(() => console.log('Do that');)
.then(() => {
console.log('Do the last thing');
})
输出如下:
Initial
Do that
Do the last thing
"Do this" 没有显示,因为抛出的错误导致了一个 rejection。
一个 Promise 链在出现异常后停止,向下寻找 .catch() 处理程序。这是模仿同步语法实现的。
在 ES2017 标准的 async/await 中体现地淋漓尽致:
async function foo() {
try {
const result = await doSomething();
const newResult = await doSomethingElse(result);
const finalResult = await doThirdThing(newResult);
} catch(error) {
failureCallback(error);
}
}
这是在 Promise 基础上构建的。
理想情况下,所有异步函数都返回 Promise 对象,但有些 API 依旧需要传入成功或失败的回调。最经典的就是 setTimeout() 函数。
混用旧式回调和 Promise 将无法捕获旧式回调中的错误,使用 Promise 包裹。最好将有问题的函数包裹在尽量底层,并且不再直接调用它们:
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
wait(1000).then(() => saySomething('1 seconds')).catch(failureCallback);
Promise.resolve() 和 Promise.reject() 是主动创建一个已经 resolved 或 rejected 的 Promise 的方法,有时很有用。
Promise.all() 和 Promise.race() 是并列运行异步操作的两个工具:
Promise.all([func1(), func2(), func3()])
.then(([result1, result2, result3])) => {/* some code */});
这样写:
[func1, func2, func3].reduce((p, f) => p.then(f), Promise.resolve())
.then(result3 => {/* use result3 */});
相当于队列链式调用:
Promise.resolve().then(func1).then(func2).then(func3).then((result3) => {/**/});
可抽象成复用的函数:
let applyAsync = (acc, val) => acc.then(val);
let composeAsync = (...funcs) => x => funcs.reduce(applyAsync, Promise.resolve(x));
composeAsync 可接受任意数量的函数作为参数,然后返回一个可接受一个初始值传入组合传递的新函数:
const transformData = composeAsync(func1, func2, func3);
const result3 = transformData(data);
使用 async/await 语法更加便捷:
let result;
for (const f of [func1, func2, func3]) {
result = await f(result);
}
/* use the last result */
即使一个 Promise 已经是 resolved 状态,也会在当前任务队列结束后调用(异步调用)。
简单的 Promise 连最好不要嵌套。
嵌套是一种限制 .catch() 作用域的声明,嵌套的 .catch() 方法只会捕获其作用域及前面的错误。如果正确使用,可以更准确地修复错误:
doSomethingCritical()
.then(result => doSomethingOptional()
.then(optionalResult => doSomethingExtraNice(optionalResult))
.catch(e => {})) // Ignore if optional stuff fails; proceed.
.then(() => moreCriticalStuff())
.catch(e => console.log("Critical failure: " + e.message));
这里内部的 .catch() 方法只会捕获来自 doSomethingOptional() 和 doSomethingExtraNice() 的错误,而且是在 moreCriticalStuff() 继续执行后;如果 moreCriticalStuff() 失败,其错误只会被外部的 .catch() 方法捕获。
应避免常见的 Promise 链错误:
永远使用 try-catch 块包裹 await 语句!
asycn function 用于定义一个异步函数(通过事件循环异步执行),其会通过一个隐式的 Promise 返回结果:
async function asynCall() {
const resolveAfter2Seconds = (ms) => {setTimeout(() => {console.log('resolve');}, ms); return 'ok';};
console.log('calling');
let result = await resolveAfter2Seconds(2000);
console.log(result);
return 'resolved';
}
asynCall();
//calling
//ok
//Promise {<resolved>: "resloved"}
//'resolve' after 2s for setTimeout
async 函数的返回一定是一个 Promise 对象,且 resolved 的值就是 return 语句的值(没有则 resolve(undefined)),或以 async 抛出的异常来 rejected(返回值被包裹进该 Promise)。
await 操作符用于等待一个 Promise 对象。
await 表达式会暂停 async 函数的执行,等待一个 Promise 的结果;如果后面跟的不是一个 Promise,则将表达式的 value 转化为一个 resolved Promise;如果 Promise 处理异常,则 throw。
如果跟的 Promise 成功,则继续执行,await 表达式的 value 为 Promise 的 value。
如果跟的 Promise 失败,抛出 rejected 的 value。
看做通过一个 Promise.then(value => value, err => err) 得到返回值。
只有在 async 函数内部能使用 await。
function getProcessedData(url) {
return downloadData(url) // returns a promise
// if rejected or throw
.catch(e => {
return downloadFallbackData(url) // returns a promise
})
// finally return
.then(v => {
return processDataInWorker(v); // returns a promise
});
}
可以改写成一个有 try-catch 块的 async 函数:
async function getProcessedData(url) {
let v;
try {
v = await downloadData(url);
} catch(e) {
v = await downloadFallbackData(url);
}
return processDataInWorker(v);
}
return 语句中无 await 声明,因为 async 函数的返回值会被隐式包裹进 Promise.resolve。