如有侵权请联系删除
前例
Promise是抽象异步处理对象以及对其进行各种操作的组件。
Promise把类似的异步处理对象和处理规则进行规范化, 并按照采用统一的接口来编写,而采取规定方法之外的写法都会出错。
下面是使用了Promise进行异步处理的一个例子
1var promise = getAsyncPromise("fileA.txt");
2promise.then(function(result){
3 // 获取文件内容成功时的处理
4}).catch(function(error){
5 // 获取文件内容失败时的处理
6});
我们可以向这个预设了抽象化异步处理的promise对象, 注册这个promise对象执行成功时和失败时相应的回调函数。除promise对象规定的方法(这里的 then 或 catch)以外的方法都是不可以使用的,所以,promise的功能是可以将复杂的异步处理轻松地进行模式化。
像 Promise 这样的全局对象还拥有一些静态方法,包括 Promise.all() 还有 Promise.resolve() 等在内。
Promise
Promise类似于 XMLHttpRequest,从构造函数 Promise 来创建一个新建新promise对象作为接口。
1var promise = new Promise(function(resolve, reject) {
2 // 异步处理
3 // 处理结束后、调用resolve 或 reject
4});
对通过new生成的promise对象为了设置其值在 resolve(成功) / reject(失败)时调用的回调函数 可以使用promise.then() 实例方法。
1promise.then(onFulfilled, onRejected)
resolve(成功)时,
onFulfilled 会被调用。
reject(失败)时,
onRejected 会被调用。
onFulfilled、onRejected 两个都为可选参数。
异常的处理
promise.catch(onRejected)
promise-workflow.js
1function asyncFunction() {
2
3 return new Promise(function (resolve, reject) {
4 setTimeout(function () {
5 resolve('Async Hello world');
6 }, 16);
7 });
8}
9
10asyncFunction().then(function (value) {
11 console.log(value); // => 'Async Hello world'
12}).catch(function (error) {
13 console.log(error);
14});
new Promise构造器之后,会返回一个promise对象;
asyncFunction为promise对象用设置 .then 调用返回值时的回调函数。
当然,像promise.then(onFulfilled, onRejected) 的方法声明一样, 如果不使用catch 方法只使用 then方法的话,如下所示的代码也能完成相同的工作。
1asyncFunction().then(function (value) {
2 console.log(value);
3}, function (error) {
4 console.log(error);
5});
我们的任务是用Promise来通过异步处理方式来获取XMLHttpRequest(XHR)的数据。
xhr-promise.js
1function getURL(URL) {
2 return new Promise(function (resolve, reject) {
3 var req = new XMLHttpRequest();
4 req.open('GET', URL, true);
5 req.onload = function () {
6 if (req.status === 200) {
7 resolve(req.responseText);
8 } else {
9 reject(new Error(req.statusText));
10 }
11 };
12 req.onerror = function () {
13 reject(new Error(req.statusText));
14 };
15 req.send();
16 });
17}
18// 运行示例
19var URL = "http://httpbin.org/get";
20getURL(URL).then(function onFulfilled(value){
21 console.log(value);
22}).catch(function onRejected(error){
23 console.error(error);
24});
异步操作
1var promise = new Promise(function (resolve){
2 console.log("inner promise"); // 1
3 resolve(42);
4});
5promise.then(function(value){
6 console.log(value); // 3
7});
8console.log("outer promise"); // 2
输出顺序为1,2,3。
同步调用和异步调用同时存在导致的混乱。
mixed-onready.js会根据执行时DOM是否已经装载完毕来决定是对回调函数进行同步调用还是异步调用。
1function onReady(fn) {
2 var readyState = document.readyState;
3 if (readyState === 'interactive' || readyState === 'complete') {
4 fn();
5 } else {
6 window.addEventListener('DOMContentLoaded', fn);
7 }
8}
9onReady(function () {
10 console.log('DOM fully loaded and parsed');
11});
12console.log('==Starting==');
如果在调用onReady之前DOM已经载入的话,对回调函数进行同步调用。
如果在调用onReady之前DOM还没有载入的话,通过注册 DOMContentLoaded 事件监听器来对回调函数进行异步调用。
因此,如果这段代码在源文件中出现的位置不同,在控制台上打印的log消息顺序也会不同。
为了解决这个问题,我们可以选择统一使用异步调用的方式。
1function onReady(fn) {
2 var readyState = document.readyState;
3 if (readyState === 'interactive' || readyState === 'complete') {
4 setTimeout(fn, 0);
5 } else {
6 window.addEventListener('DOMContentLoaded', fn);
7 }
8}
9onReady(function () {
10 console.log('DOM fully loaded and parsed');
11});
12console.log('==Starting==');
前面我们看到的 promise.then 也属于此类,为了避免上述中同时使用同步、异步调用可能引起的混乱问题,Promise在规范上规定 Promise只能使用异步调用方式 。
1function onReadyPromise() {
2 return new Promise(function (resolve, reject) {
3 var readyState = document.readyState;
4 if (readyState === 'interactive' || readyState === 'complete') {
5 resolve();
6 } else {
7 window.addEventListener('DOMContentLoaded', resolve);
8 }
9 });
10}
11onReadyPromise().then(function () {
12 console.log('DOM fully loaded and parsed');
13});
14console.log('==Starting==');
由于Promise保证了每次调用都是以异步方式进行的,所以我们在实际编码中不需要调用 setTimeout 来自己实现异步调用。
promise chain(方法链)
1function taskA() {
2 console.log("Task A");
3 throw new Error("throw Error @ Task A")
4}
5function taskB() {
6 console.log("Task B");// 不会被调用
7}
8function onRejected(error) {
9 console.log(error);// => "throw Error @ Task A"
10}
11function finalTask() {
12 console.log("Final Task");
13}
14
15var promise = Promise.resolve();
16promise
17 .then(taskA)
18 .then(taskB)
19 .catch(onRejected)
20 .then(finalTask);
执行这段代码我们会发现 Task B 是不会被调用的。
promise chain 中如何传递参数?
这时候如果 Task A 想给 Task B 传递一个参数该怎么办呢?
答案非常简单,那就是在 Task A 中 return 的返回值,会在 Task B 执行时传给它。
1function doubleUp(value) {
2 return value * 2;
3}
4function increment(value) {
5 return value + 1;
6}
7function output(value) {
8 console.log(value);// => (1 + 1) * 2
9}
10
11var promise = Promise.resolve(1);
12promise
13 .then(increment)
14 .then(doubleUp)
15 .then(output)
16 .catch(function(error){
17 // promise chain中出现异常的时候会被调用
18 console.error(error);
19 });
Promise#catch
IE 8浏览器实用catch(ECMAScript3保留字)作为属性会出现 identifier not found,ECMAScript5保留字可以作为属性。
1var promise = Promise.reject(new Error("message"));
2promise.catch(function (error) {
3 console.error(error);
4});
可以变换两种下面两种写法:
1.用 catch标识符解决冲突问题
1var promise = Promise.reject(new Error("message"));
2promise["catch"](function (error) {
3 console.error(error);
4});
2.改用then代替
1var promise = Promise.reject(new Error("message"));
2promise.then(undefined, function (error) {
3 console.error(error);
4});
正确的返回返回新创建的promise对象
1function anAsyncCall() {
2 var promise = Promise.resolve();
3 return promise.then(function() {
4 // 任意处理
5 return newVar;
6 });
7}
使用Promise#then同时处理多个异步请求
1function getURL(URL) {
2 return new Promise(function (resolve, reject) {
3 var req = new XMLHttpRequest();
4 req.open('GET', URL, true);
5 req.onload = function () {
6 if (req.status === 200) {
7 resolve(req.responseText);
8 } else {
9 reject(new Error(req.statusText));
10 }
11 };
12 req.onerror = function () {
13 reject(new Error(req.statusText));
14 };
15 req.send();
16 });
17}
18var request = {
19 comment: function getComment() {
20 return getURL('http://azu.github.io/promises-book/json/comment.json').then(JSON.parse);
21 },
22 people: function getPeople() {
23 return getURL('http://azu.github.io/promises-book/json/people.json').then(JSON.parse);
24 }
25 };
26function main() {
27 function recordValue(results, value) {
28 results.push(value);
29 return results;
30 }
31 // [] 用来保存初始化的值
32 var pushValue = recordValue.bind(null, []);
33 return request.comment().then(pushValue).then(request.people).then(pushValue);
34}
35// 运行的例子
36main().then(function (value) {
37 console.log(value);
38}).catch(function(error){
39 console.error(error);
40});
为了应对这种需要对多个异步调用进行统一处理的场景,Promise准备了 Promise.all 和 Promise.race 这两个静态方法。
Promise.all
Promise.all 接收一个 promise对象的数组作为参数,当这个数组里的所有promise对象全部变为resolve或reject状态的时候,它才会去调用 .then 方法。
1function getURL(URL) {
2 return new Promise(function (resolve, reject) {
3 var req = new XMLHttpRequest();
4 req.open('GET', URL, true);
5 req.onload = function () {
6 if (req.status === 200) {
7 resolve(req.responseText);
8 } else {
9 reject(new Error(req.statusText));
10 }
11 };
12 req.onerror = function () {
13 reject(new Error(req.statusText));
14 };
15 req.send();
16 });
17}
18var request = {
19 comment: function getComment() {
20 return getURL('http://azu.github.io/promises-book/json/comment.json').then(JSON.parse);
21 },
22 people: function getPeople() {
23 return getURL('http://azu.github.io/promises-book/json/people.json').then(JSON.parse);
24 }
25 };
26function main() {
27 return Promise.all([request.comment(), request.people()]);
28}
29// 运行示例
30main().then(function (value) {
31 console.log(value);
32}).catch(function(error){
33 console.log(error);
34});
这个例子的执行方法和 前面的例子 一样。 不过Promise.all 在以下几点和之前的例子有所不同。
main中的处理流程显得非常清晰。
Promise.all 接收 promise对象组成的数组作为参数。
Promise.race
接着我们来看看和 Promise.all 类似的对多个promise对象进行处理的 Promise.race 方法。
它的使用方法和Promise.all一样,接收一个promise对象数组为参数。
Promise.all 在接收到的所有的对象promise都变为 FulFilled 或者 Rejected 状态之后才会继续进行后面的处理, 与之相对的是 Promise.race 只要有一个promise对象进入 FulFilled 或者 Rejected 状态的话,就会继续进行后面的处理。
1// `delay`毫秒后执行resolve
2function timerPromisefy(delay) {
3 return new Promise(function (resolve) {
4 setTimeout(function () {
5 resolve(delay);
6 }, delay);
7 });
8}
9// 任何一个promise变为resolve或reject 的话程序就停止运行
10Promise.race([
11 timerPromisefy(1),
12 timerPromisefy(32),
13 timerPromisefy(64),
14 timerPromisefy(128)
15]).then(function (value) {
16 console.log(value); // => 1
17});