天天看点

【JS】97-学习 Promise,一些代码收集理解

【JS】97-学习 Promise,一些代码收集理解

如有侵权请联系删除

前例

【JS】97-学习 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

【JS】97-学习 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 两个都为可选参数。

异常的处理

【JS】97-学习 Promise,一些代码收集理解

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

异步操作

【JS】97-学习 Promise,一些代码收集理解
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(方法链)

【JS】97-学习 Promise,一些代码收集理解
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);      
【JS】97-学习 Promise,一些代码收集理解

执行这段代码我们会发现 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    });      
【JS】97-学习 Promise,一些代码收集理解

Promise#catch

【JS】97-学习 Promise,一些代码收集理解

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对象

【JS】97-学习 Promise,一些代码收集理解
1function anAsyncCall() {
2    var promise = Promise.resolve();
3    return promise.then(function() {
4        // 任意处理
5        return newVar;
6    });
7}      

使用Promise#then同时处理多个异步请求

【JS】97-学习 Promise,一些代码收集理解
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

【JS】97-学习 Promise,一些代码收集理解

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

【JS】97-学习 Promise,一些代码收集理解

接着我们来看看和 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});