学习JavaScript Promise,一些代码收集理解

前例:

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});

参考:http://liubin.org/promises-book/#chapter3-promise-testing 前两节

如果有其他疑问或是看法可以在评论里或投稿跟小编进行讨论,
也可以关注微信公众号【筑梦前端】进行投稿,与小编一起探讨更多的编程知识。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值