Promise异步操作基础

如有侵权请联系删除

Promise是抽象异步处理对象以及对其进行各种操作的组件。

Promise把类似的异步处理对象和处理规则进行规范化, 并按照采用统一的接口来编写,而采取规定方法之外的写法都会出错。

下面是使用了Promise进行异步处理的一个例子

var promise = getAsyncPromise("fileA.txt");
    promise.then(function(result){
            // 获取文件内容成功时的处理
        }).catch(function(error){
            // 获取文件内容失败时的处理
        });

我们可以向这个预设了抽象化异步处理的promise对象, 注册这个promise对象执行成功时和失败时相应的回调函数。除promise对象规定的方法(这里的 then 或 catch)以外的方法都是不可以使用的,所以,promise的功能是可以将复杂的异步处理轻松地进行模式化。

像 Promise 这样的全局对象还拥有一些静态方法,包括 Promise.all() 还有 Promise.resolve() 等在内。

Promise

Promise类似于 XMLHttpRequest,从构造函数 Promise 来创建一个新建新promise对象作为接口。

var promise = new Promise(function(resolve, reject) {
        // 异步处理
        // 处理结束后、调用resolve 或 reject
    });

对通过new生成的promise对象为了设置其值在 resolve(成功) / reject(失败)时调用的回调函数 可以使用promise.then() 实例方法。

promise.then(onFulfilled, onRejected)

resolve(成功)时,
onFulfilled 会被调用。

reject(失败)时,
onRejected 会被调用。
onFulfilled、onRejected 两个都为可选参数。

异常的处理

promise.catch(onRejected)

promise-workflow.js

function asyncFunction() {
        return new Promise(function (resolve, reject) {
            setTimeout(function () {
                resolve('Async Hello world');
            }, 16);
        });
    }
    asyncFunction().then(function (value) {
        console.log(value);    // => 'Async Hello world'
    }).catch(function (error) {
        console.log(error);
    });

new Promise构造器之后,会返回一个promise对象;
asyncFunction为promise对象用设置 .then 调用返回值时的回调函数。

当然,像promise.then(onFulfilled, onRejected) 的方法声明一样, 如果不使用catch 方法只使用 then方法的话,如下所示的代码也能完成相同的工作。

asyncFunction().then(function (value) {
        console.log(value);
    }, function (error) {
        console.log(error);
    });

我们的任务是用Promise来通过异步处理方式来获取XMLHttpRequest(XHR)的数据。
xhr-promise.js

function getURL(URL) {
        return new Promise(function (resolve, reject) {
            var req = new XMLHttpRequest();
            req.open('GET', URL, true);
            req.onload = function () {
                if (req.status === 200) {
                    resolve(req.responseText);
                } else {
                    reject(new Error(req.statusText));
                }
            };
            req.onerror = function () {
                reject(new Error(req.statusText));
            };
            req.send();
        });
    }
    // 运行示例
    var URL = "http://httpbin.org/get";
    getURL(URL).then(function onFulfilled(value){
        console.log(value);
    }).catch(function onRejected(error){
        console.error(error);
    });

异步操作

var promise = new Promise(function (resolve){
        console.log("inner promise"); // 1
        resolve(42);
    });
    promise.then(function(value){
        console.log(value); // 3
    });
    console.log("outer promise"); // 2

输出顺序为1,2,3。
同步调用和异步调用同时存在导致的混乱。

mixed-onready.js会根据执行时DOM是否已经装载完毕来决定是对回调函数进行同步调用还是异步调用。

function onReady(fn) {
        var readyState = document.readyState;
        if (readyState === 'interactive' || readyState === 'complete') {
            fn();
        } else {
            window.addEventListener('DOMContentLoaded', fn);
        }
    }
    onReady(function () {
        console.log('DOM fully loaded and parsed');
    });
    console.log('==Starting==');

如果在调用onReady之前DOM已经载入的话,对回调函数进行同步调用。

如果在调用onReady之前DOM还没有载入的话,通过注册 DOMContentLoaded 事件监听器来对回调函数进行异步调用。

因此,如果这段代码在源文件中出现的位置不同,在控制台上打印的log消息顺序也会不同。

为了解决这个问题,我们可以选择统一使用异步调用的方式。

function onReady(fn) {
        var readyState = document.readyState;
        if (readyState === 'interactive' || readyState === 'complete') {
            setTimeout(fn, 0);
        } else {
            window.addEventListener('DOMContentLoaded', fn);
        }
    }
    onReady(function () {
        console.log('DOM fully loaded and parsed');
    });
    console.log('==Starting==');

前面我们看到的 promise.then 也属于此类,为了避免上述中同时使用同步、异步调用可能引起的混乱问题,Promise在规范上规定 Promise只能使用异步调用方式 。

function onReadyPromise() {
        return new Promise(function (resolve, reject) {
            var readyState = document.readyState;
            if (readyState === 'interactive' || readyState === 'complete') {
                resolve();
            } else {
                window.addEventListener('DOMContentLoaded', resolve);
            }
        });
    }
    onReadyPromise().then(function () {
        console.log('DOM fully loaded and parsed');
    });
    console.log('==Starting==');

由于Promise保证了每次调用都是以异步方式进行的,所以我们在实际编码中不需要调用 setTimeout 来自己实现异步调用。

promise chain(方法链)

function taskA() {
        console.log("Task A");
        throw new Error("throw Error @ Task A")
    }
    function taskB() {
        console.log("Task B");// 不会被调用
    }
    function onRejected(error) {
        console.log(error);// => "throw Error @ Task A"
    }
    function finalTask() {
        console.log("Final Task");
    }
    var promise = Promise.resolve();
    promise
        .then(taskA)
        .then(taskB)
        .catch(onRejected)
        .then(finalTask);

Promise异步操作基础_第1张图片
执行这段代码我们会发现 Task B 是不会被调用的。

promise chain 中如何传递参数?

这时候如果 Task A 想给 Task B 传递一个参数该怎么办呢?

答案非常简单,那就是在 Task A 中 return 的返回值,会在 Task B 执行时传给它。

function doubleUp(value) {
        return value * 2;
    }
    function increment(value) {
        return value + 1;
    }
    function output(value) {
        console.log(value);// => (1 + 1) * 2
    }
    var promise = Promise.resolve(1);
    promise
        .then(increment)
        .then(doubleUp)
        .then(output)
        .catch(function(error){
            // promise chain中出现异常的时候会被调用
            console.error(error);
        });

Promise异步操作基础_第2张图片

Promise#catch

IE 8浏览器实用catch(ECMAScript3保留字)作为属性会出现 identifier not found,ECMAScript5保留字可以作为属性。

var promise = Promise.reject(new Error("message"));
    promise.catch(function (error) {
            console.error(error);
        });

可以变换两种下面两种写法:

1.用 catch标识符解决冲突问题

var promise = Promise.reject(new Error("message"));
    promise["catch"](function (error) {
            console.error(error);
        });

2.改用then代替

var promise = Promise.reject(new Error("message"));
    promise.then(undefined, function (error) {
            console.error(error);
        });

正确的返回返回新创建的promise对象

function anAsyncCall() {
        var promise = Promise.resolve();
        return promise.then(function() {
            // 任意处理
            return newVar;
        });
    }

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

function getURL(URL) {
        return new Promise(function (resolve, reject) {
            var req = new XMLHttpRequest();
            req.open('GET', URL, true);
            req.onload = function () {
                if (req.status === 200) {
                    resolve(req.responseText);
                } else {
                    reject(new Error(req.statusText));
                }
            };
            req.onerror = function () {
                reject(new Error(req.statusText));
            };
            req.send();
        });
    }
    var request = {
        comment: function getComment() {
            return getURL('http://azu.github.io/promises-book/json/comment.json').then(JSON.parse);
        },
        people: function getPeople() {
            return getURL('http://azu.github.io/promises-book/json/people.json').then(JSON.parse);
        }
    };
    function main() {
        function recordValue(results, value) {
            results.push(value);
            return results;
        }
        // [] 用来保存初始化的值
        var pushValue = recordValue.bind(null, []);
        return request.comment().then(pushValue).then(request.people).then(pushValue);
    }
    // 运行的例子
    main().then(function (value) {
        console.log(value);
    }).catch(function(error){
        console.error(error);
    });

为了应对这种需要对多个异步调用进行统一处理的场景,Promise准备了 Promise.all 和 Promise.race 这两个静态方法。

Promise.all

Promise.all 接收一个 promise对象的数组作为参数,当这个数组里的所有promise对象全部变为resolve或reject状态的时候,它才会去调用 .then 方法。

function getURL(URL) {
        return new Promise(function (resolve, reject) {
            var req = new XMLHttpRequest();
            req.open('GET', URL, true);
            req.onload = function () {
                if (req.status === 200) {
                    resolve(req.responseText);
                } else {
                    reject(new Error(req.statusText));
                }
            };
            req.onerror = function () {
                reject(new Error(req.statusText));
            };
            req.send();
        });
    }
    var request = {
        comment: function getComment() {
            return getURL('http://azu.github.io/promises-book/json/comment.json').then(JSON.parse);
        },
        people: function getPeople() {
            return getURL('http://azu.github.io/promises-book/json/people.json').then(JSON.parse);
        }
    };
    function main() {
        return Promise.all([request.comment(), request.people()]);
    }
    // 运行示例
    main().then(function (value) {
        console.log(value);
    }).catch(function(error){
        console.log(error);
    });

这个例子的执行方法和 前面的例子 一样。 不过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 状态的话,就会继续进行后面的处理。

// `delay`毫秒后执行resolve
    function timerPromisefy(delay) {
        return new Promise(function (resolve) {
            setTimeout(function () {
                resolve(delay);
            }, delay);
        });
    }
    // 任何一个promise变为resolve或reject 的话程序就停止运行
    Promise.race([
        timerPromisefy(1),
        timerPromisefy(32),
        timerPromisefy(64),
        timerPromisefy(128)
    ]).then(function (value) {
        console.log(value);    // => 1
    });

参考:http://liubin.org/promises-book/#chapter3-promise-testing 前两节
转载自:筑梦前端(wx:tanzhouweb)

你可能感兴趣的:(Promise异步操作基础)