Promise
首先先说说Promise,多个异步调用可以用promise链式方法来很直观的表示。
doSomething().then(function(result) {
return doSomethingElse(result);
})
.then(function(newResult) {
return doThirdThing(newResult);
})
.then(function(finalResult) {
console.log('Got the final result: ' + finalResult);
})
.catch(failureCallback);
也可以用 arrow functions(箭头函数)来表示:
doSomething()
.then(result => doSomethingElse(result))
.then(newResult => doThirdThing(newResult))
.then(finalResult => {
console.log(`Got the final result: ${finalResult}`);
})
.catch(failureCallback);
注意:如果想要在回调中获取上个 Promise 中的结果,上个 Promise 中必须要返回结果。
很可能会在一个回调失败之后继续使用链式操作,即 使用一个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 this whatever happened before');
});
输出结果如下:
Initial
Do that
Do this whatever happened before
一个 Promise
有以下几种状态:
- pending: 初始状态,既不是成功,也不是失败状态。
- fulfilled: 意味着操作成功完成。
- rejected: 意味着操作失败。
pending 状态的 Promise 对象可能会变为fulfilled 状态并传递一个值给相应的状态处理方法,也可能变为失败状态(rejected)并传递失败信息。当其中任一种情况出现时,Promise 对象的 then
方法绑定的处理方法(handlers )就会被调用(then方法包含两个参数:onfulfilled 和 onrejected,它们都是 Function 类型。当Promise状态为fulfilled时,调用 then 的 onfulfilled 方法,当Promise状态为rejected时,调用 then 的 onrejected 方法, 所以在异步操作的完成和绑定处理方法之间不存在竞争)。
错误传递
在之前的回调地狱示例中,你可能记得有3次failureCallback
的调用,而在 Promise 链中只有底部的一次调用。
doSomething()
.then(result => doSomethingElse(value))
.then(newResult => doThirdThing(newResult))
.then(finalResult => console.log(`Got the final result: ${finalResult}`))
.catch(failureCallback);
通常,一遇到异常抛出,promise链就会停下来,直接调用链式中的catch处理程序来继续当前执行。这看起来和同步代码的执行很相似。
在旧式回调 API 中创建 Promise
Promise
通过它的构造器从头开始创建。 只应用在包裹旧的 API。
理想状态下,所有的异步函数都已经返回 Promise 了。但有一些 API 仍然使用旧式的被传入的成功或者失败的回调。典型的例子就是setTimeout()
函数:
setTimeout(() => saySomething("10 seconds passed"), 10000);
混用旧式回调和 Promise 是会有问题的。如果 saySomething
函数失败了或者包含了编程错误,那就没有办法捕获它了。
幸运的是我们可以用 Promise 来包裹它。最好的做法是将有问题的函数包装在最低级别,并且永远不要再直接调用它们:
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
wait(10000).then(() => saySomething("10 seconds")).catch(failureCallback);
通常,Promise 的构造器会有一个可以让我们手动操作resolve和reject的执行函数。既然 setTimeout
没有真的执行失败,那么我们可以在这种情况下忽略reject。
嵌套
简便的 Promise 链式编程最好保持扁平化,不要嵌套 Promise,嵌套经常会是粗心导致的。可查阅下一节的常见错误中的例子。
嵌套 Promise 是一种可以限制 catch 语句的作用域的控制结构写法。明确来说,嵌套的 catch 仅捕捉在其之前同时还必须是其作用域的 failureres,而捕捉不到在其链式以外或者其嵌套域以外的 error。如果使用正确,那么可以实现高精度的错误修复。
doSomethingCritical()
.then(result => doSomethingOptional()
.then(optionalResult => doSomethingExtraNice(optionalResult))
.catch(e => {console.log(e.message)})) // 即使有异常也会忽略,继续运行;(最后会输出)
.then(() => moreCriticalStuff())
.catch(e => console.log("Critical failure: " + e.message));// 没有输出
注意,有些代码步骤是嵌套的,而不是一个简单的纯链式,这些语句前与后都被()包裹着。
这个内部的 catch 语句仅能捕获到 doSomethingOptional() 和 doSomethingExtraNice() 的失败,而且还是在
moreCriticalStuff() 并发运行以后。重要提醒,如果 doSomethingCritical() 失败,这个错误才仅会被最后的(外部)catch 语句捕获到。
创建Promise
Promise
对象是由关键字 new
及其构造函数来创建的。该构造函数会把一个叫做“处理器函数”(executor function)的函数作为它的参数。这个“处理器函数”接受两个函数——resolve
和 reject
——作为其参数。当异步任务顺利完成且返回结果值时,会调用 resolve
函数;而当异步任务失败且返回失败原因(通常是一个错误对象)时,会调用reject
函数。
const myFirstPromise = new Promise((resolve, reject) => {
// ?做一些异步操作,最终会调用下面两者之一:
//
// resolve(someValue); // fulfilled
// ?或
// reject("failure reason"); // rejected
});
想要某个函数?拥有promise功能,只需让其返回一个promise即可。
function myAsyncFunction(url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.onload = () => resolve(xhr.responseText);
xhr.onerror = () => reject(xhr.statusText);
xhr.send();
});
};
示例
非常简单的例子
let myFirstPromise = new Promise(function(resolve, reject){
//当异步代码执行成功时,我们才会调用resolve(...), 当异步代码失败时就会调用reject(...)
//在本例中,我们使用setTimeout(...)来模拟异步代码,实际编码时可能是XHR请求或是HTML5的一些API方法.
setTimeout(function(){
resolve("成功!"); //代码正常执行!
}, 250);
});
myFirstPromise.then(function(successMessage){
//successMessage的值是上面调用resolve(...)方法传入的值.
//successMessage参数不一定非要是字符串类型,这里只是举个例子
console.log("Yay! " + successMessage);
});
then()函数
promise.then(onCompleted, onRejected);
参数
- promise
必需。
Promise 对象。 - onCompleted
必需。
承诺成功完成时要运行的履行处理程序函数。 - onRejected
可选。
承诺被拒绝时要运行的错误处理程序函数。
await
await
操作符用于等待一个Promise
对象。它只能在异步函数 async function
中使用。
语法
[return_value] = await expression;
表达式
一个 Promise
对象或者任何要等待的值。
返回值
返回 Promise 对象的处理结果。如果等待的不是 Promise 对象,则返回该值本身。
描述
await 表达式会暂停当前 async function
的执行,等待 Promise 处理完成。若 Promise 正常处理(fulfilled),其回调的resolve函数参数作为 await 表达式的值,继续执行 async function
。
若 Promise 处理异常(rejected),await 表达式会把 Promise 的异常原因抛出。
另外,如果 await 操作符后的表达式的值不是一个 Promise,则返回该值本身。
例子
如果一个 Promise 被传递给一个 await 操作符,await 将等待 Promise 正常处理完成并返回其处理结果。
function resolveAfter2Seconds(x) {
return new Promise(resolve => {
setTimeout(() => {
resolve(x);
}, 2000);
});
}
async function f1() {
var x = await resolveAfter2Seconds(10); //必须是在async函数中使用await
console.log(x); // 10
}
f1();
如果该值不是一个 Promise,await 会把该值转换为已正常处理的Promise,然后等待其处理结果。
async function f2() {
var y = await 20;
console.log(y); // 20
}
f2();
如果 Promise 处理异常,则异常值被抛出。
async function f3() {
try {
var z = await Promise.reject(30);
} catch (e) {
console.log(e); // 30
}
}
f3();