zoukankan      html  css  js  c++  java
  • Promise 入门与使用

    Tags: ECMAScript6

    参考资料

    Promise最初被提出是在 E语言中, 它是基于并列/并行处理设计的一种编程语言。

    概念

    ES6规定,Promise对象是一个构造函数,用来生成Promise实例。

    规范:Promises -> Promises/A -> Promises/A+

    状态

    Promise

    Promise实例生成以后处于pending状态,可以用then方法分别指定Resolved状态和Reject状态的回调函数。

    ES6 Promise 使用

    API

    • Promise.prototype.constructor
    • Promise.prototype.then
    • Promise.prototype.catch
    • Promise.all
    • Promise.race
    • resolve
    • reject

    基本用法

    var promise = new Promise(function (resolve, reject) {
        setTimeout(function () {
            resolve("success");
        }, 500);
        //reject(error);
    });
    
    promise
            .then(function (value) {
                //success
                console.log(value);
                return "over"
            }, function (value) {
                //error
                console.log(value);
            });
    
    console.log("hello");
    
    //"hello"
    //"success"
    

    链式调用

    then方法执行后会返回另一个promise,它的状态会传递给下一个then,如果没有显示的在回调函数中指定返回的promise对象,那么引擎会帮你生成一个promise并且执行其resolve方法,并且把你的回调函数传入其中。

    function asynchData (msg) {
        return new Promise(function (resolve, reject) {
            setTimeout(function () {
                resolve(msg);
            }, 100);
        });
    }
    
    asynchData("step1")
            .then(function (value) {
                //success
                console.log(value);
                return asynchData("step2")
            })
            .then(function (value) {
                console.log(value);
            })
    
    //"step1"
    //"step2"
    
    asynchData("step2")
            .then(function (value) {
                //success
                console.log(value);
                return "success";
            })
            .then(function (value) {
                console.log(value);
            })
    //"step1"
    //"success"
    

    本身then、catch方法默认的返回是一个新的Promise对象

    function asynchData (msg) {
        return new Promise(function (resolve, reject) {
            setTimeout(function () {
                resolve(msg);
            }, 100);
        });
    }
    
    var stepOne = asynchData("stepone");
    var stepTwo = stepOne.then(function (val) {
        console.log(val);//stepone
    });
    var stepThree = stepTwo.then(function (val) {
        console.log(val);//undefined
    });
    var stepThree2 = stepTwo.then(function (val) {
        console.log(val);//undefined
    });
    var catchStep = stepThree.catch(function (err) {
        console.log(err);
    });
    
    console.log(stepTwo instanceof Promise);//true
    console.log(stepThree instanceof Promise);//true
    console.log(stepThree2 instanceof Promise);//true
    console.log(catchStep instanceof Promise);//true
    

    error处理:catch

    其实catch是then(null, function(){...})的别名,实质上是相等的。注意一点,没有使用catch方法指定错误处理的回调函数,Promise对象抛出的错误不会传递到外层代码,即不会有任何反应。所以为了不给自己找麻烦尽量编写catch方法

    function asynchData (msg) {
        return new Promise(function (resolve, reject) {
            setTimeout(function () {
                //Promise指定在下一轮“事件循环”再抛出错误,结果由于没有指定catch语句,就冒泡到最外层,成了未捕获的错误。
                throw new Error('async error!');
            }, 1000);
    
            throw new Error('error!');
        });
    }
    
    asynchData("step1")
            .then(function (value) {
                //success
                console.log(value);
            })
            .catch(function (err) {
                console.log(err);
                //如果Promise状态已经变成resolved,再抛出错误是无效的。
                //Promise对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获。
            });
    
    //"Error: error!"
    
    asynchData("step2")
            .then(function (value) {
                //success
                console.log(value);
            })
            .then(null, function (err) {
                console.log(err);
            });
    
    //"Error: error!"
    

    这里有一个要注意的点:如果你一旦指定catch方法或者你在then方法里指定了reject方法,那么你指定的catch方法会捕获住异常,并且异常不再继续传递下去,无论你在catch里返回什么!除非catch方法里还有报错、或者返回一个reject状态的promise

    function asynchData (msg) {
        return new Promise(function (resolve, reject) {
            setTimeout(function () {
                reject(msg);
            }, 100);
        });
    }
    
    asynchData("I am error")
            .then(function () {
                //resolve callback
            }, function (err) {
                //reject callback
                console.log(err + "from reject callback")
            })
            .catch(function (err) {
                //err callback
                console.log(err + " from catch callback");//catch callback无法执行,因为上一个then已经指定了reject处理方法
            })
    
    // I am errorfrom reject callback
    
    asynchData("I am error too")
            .then(function () {
                //resolve callback
            }, function (err) {
                //reject callback
                console.log(err + "from reject callback");
                throw new Error("I from reject callback error");
            })
            .catch(function (err) {
                //err callback
                console.log(err + " from catch callback");
                //catch callback会被执行,并且接收的错误是从上一个reject方法返回的
                //上一个then方法抛出异常,then返回一个reject状态的promise,并且被下一个catch方法捕获住
            })
    
    //I am error toofrom reject callback
    //Error: I from reject callback error from catch callback
    

    同步or异步

    试想下如果让你写一个api,其中有一个参数是callback形式传入,那么方法体内该是以何种形式执行callback呢?是同步呢还是异步执行呢?为了保持API的统一,我们定义callback在方法里都是以异步方式执行的。

    关于这个问题,在 Effective JavaScript 的 第67项 不要对异步回调函数进行同步调用 中也有详细介绍。

    绝对不能对异步回调函数(即使在数据已经就绪)进行同步调用。

    如果对异步回调函数进行同步调用的话,处理顺序可能会与预期不符,可能带来意料之外的后果。

    对异步回调函数进行同步调用,还可能导致栈溢出或异常处理错乱等问题。

    如果想在将来某时刻调用异步回调函数的话,可以使用 setTimeout 等异步API。

    Effective JavaScript
    — David Herman

    了避免同时使用同步、异步调用可能引起的混乱问题,Promise在规范上规定 Promise只能使用异步调用方式。

    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
    
    // inner promise
    // outer promise
    // 42
    

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

    多个promise:all

    function asynchData (msg, toReject) {
        return new Promise(function (resolve, reject) {
            setTimeout(function () {
                if (toReject) {
                    reject(msg + " failure!!!");
                } else {
                    resolve(msg);
                }
            }, 100);
        });
    }
    
    Promise.all([asynchData("step1"), asynchData("step2"), asynchData("step2")]).then(function (value) {
        console.log(value);//["step1", "step2", "step2"]
    });
    
    Promise.all([asynchData("step1", true), asynchData("step2", true), asynchData("step2")]).then(
            function (value) {
                console.log(value);//["step1", "step2", "step2"]
            },
            function (value) {
                console.log(value);//step1 failure!!!
                //之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值
            }
    );
    

    处理reject

    function asynchData (msg, toReject) {
        return new Promise(function (resolve, reject) {
            setTimeout(function () {
                if (toReject) {
                    reject(msg + " failure!!!");
                } else {
                    resolve(msg);
                }
            }, 100);
        });
    }
    
    //之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的Promise实例的返回值,就传递给p的回调函数
    Promise.race([asynchData("step1"), asynchData("step2"), asynchData("step2")]).then(function (value) {
        console.log(value);//"step1"
    });
    

    一些例子

    我们定义A、B、C三个方法,并且都是异步的。我们希望A成功后执行B,失败执行C,B失败后执行C。想想改如何实现呢?

    function asynchData (msg, toFail) {
        return new Promise(function (resolve, reject) {
            console.log(msg);
            if (toFail) {
                reject();
            } else {
                resolve();
            }
        });
    }
    
    asynchData("A")
            .then(function () {
                return asynchData("B");
            })
            .then(null, function () {
                asynchData("C")
            });
    

    这样写是不是就达到目的了?

    Deferred & Promise 关系

    我们经常见到jquery.Deferred,但是和Promise有什么关系呢?Deferred和Promise不同,它没有共通的规范,每个Library都是根据自己的喜好来实现的。

    • Deferred 拥有 Promise
    • Deferred 具备对 Promise的状态进行操作的特权方法

    相信我们使用promise后会给我们带来耳目一新的感觉,减少我们的callback hell的出现频率,大大的增强我们代码的可维护性!

  • 相关阅读:
    iOS APP上线流程
    iOS开发:cocoapods的使用
    Swift:函数和闭包
    OC中单例的各种写法及基本讲解
    iOS:死锁
    iOS传值方式:属性,代理,block,单例,通知
    iOS支付
    Binary Tree Preorder Traversal——经典算法的迭代求解(前序,中序,后序都在这里了)
    Largest Number——STL的深层理解
    const、volatile、mutable的用法
  • 原文地址:https://www.cnblogs.com/xiaoniuzai/p/6534374.html
Copyright © 2011-2022 走看看