zoukankan      html  css  js  c++  java
  • Promise简介

    了解Promise

    ECMAScript 6语言标准已于2015年6月发布,ECMAScript 6中的Promise规范来源于Promises/A+社区。在JavaScript中已经早有Promise的实现,在Node.js中也有多个实现了Promise规范的 npm 模块。ECMAScript 6将其写进了语言标准,对Promise用法进行了规范,并提供了原生的Promise对象,在Firefox和Chrome最新版本的浏览器上,己经提供了对Promise的支持。本文主要以ES6 Promises为基础的,介绍Promise对象的相关知识。

    JavaScript异步处理

    JavaScript是一种采用事件驱动的脚本语言,在JavaScript中,处理异步都是使用callback的方式,例如:Node.js就是基于回调函数机制进行异步处理的。随着JavaScript的使用日益广泛,其开发模式也日渐成熟,随之也就产生了两种模块管理规范:AMD (Asynchronous Module Definition)规范和CMD (Common Module Definition,即:CommonJS)规范,在CommonJS中,就包括了用于异步处理的Promise规范。

    示例,jQuery的GET请求中异步处理:

    $.get("http://baidu.com", function(result){
        //GET请求成功后的callback回调函数
    });

    什么是Promise

    Promise是一个用于异步处理对象,其中包含了对异步进行各种操作的组件。Promise把JavaScript中的异步处理对象和处理规则进行了规范化,并按照统一的接口来编写,使用规定方法之外的写法会出现错误。使用Promise规范处理异步编程,相对比较简单和易于理解。

    Promise对象具有一下两个特点:

    1、对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:Pending(进行中),Resolved(以完成,又称:Fulfilled)和Rejected(已失败)。只有异步操作的结果可以决定当前是那种状态,任何其他的操作都无法改变这个状态。

    2、一旦状态改变就不会变,任何时候都可以得到这个结果。Promise对象的状态改变有两种可能:从Pending变成Resolved和从Pending变成Rejected。只要其中之一发生变化,状态就固定不会变了,只会保证一个结果,就算改变已经发生,你在对Promise对象添加回调函数也只会得到这个结果。这个与事件(Event)不同,时间的的特点是,如果你错过了它,再去监听是得不到结果的。

    Promise对象同样也有自己的缺点,首先,无法取消Promise。一旦建立就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise内部抛出的错误不会反映到外部(所以错误处理的回调函数是很重要的)。再者,当处于Pending状态时,无法得知目前进展到哪一阶段了,是刚刚开始还是即将完成。

    示例,使用Promise进行异步处理:

    //获取一个Promise对象
    var promise = romiseReadFile("file.txt"); 
    promise.then(function(result){
        // 操作成功时的处理
    }).catch(function(error){
        // 操作失败时的处理
    });

    以上代码,注册Promise对象执行成功时和失败时相应的回调函数。与回调函数处理机制不同的是,使用Promise进行处理的时候,必须按照接口规定的方法编写处理代码,即:除promise对象规定(如上面代码的thencatch方法)以外的方法都是不可以使用的, 不能像回调函数方式那样可以自己自由的定义回调函数的参数,而必须按照固定、统一的编程方式来编写代码。

    基于Promise规范,为异步处理统一了异步处理接口, 这样就形成了基于接口形成了不同的异步处理模式。因此,可以将复杂的异步处理模式化、简单化。

     Promise/A+规范

    由于ECMAScript 6中的Promise规范来源于Promise/A+规范,了解Promise/A+规范有助更好的学习和理解Promise。下面是对Promise/A+规范的简单介绍:

    • 一个promise有一到三种状态:pending(等待)、fulfilled或resolved(成功-已完成)、rejected(失败-已拒绝)
    • 一个promise的状态只可能从等待完成或者拒绝状态,不能逆向转换,同时完成拒绝状态不能相互转换
    • promise实例必须实现then方法,而且then必须返回一个promise,同一个promisethen可以调用多次,并且回调的执行顺序跟它们被定义时的顺序一致
    • then方法接受两个参数:第一个参数是成功时的回调,在promise等待养成转换到完成态时调用。另一个是失败时的回调,在promise等待状态转换到拒绝状态时调用。同时,then可以接受另一个promise传入,也接受一个类似then的对象或方法,即:thenable对象

    Promise对象简介

    ECMAScript 6 Promise规范中,Promise对象包括三部分:构造函数、实例方法、静态方法。

    构造函数:Promise

    要创建一个Promise对象实例,需要调用构造函数Promise并使用new关键字来创建,示例如下:

    var promise = new Promise(function(resolve, reject) {
        // 异步处理回调函数
        // 处理结束后调用resolve 或 reject方法
        if (操作成功){
            resolve(value);
        } else {
            reject(error);
        }
    });

    Promise构造函数接受一个函数作为传入参数,该函数包含两个参数:resolvereject,这两个参数是两个回调函数,分别用于执行成功和执行失败时的回调。

    • resolve函数:Promise对象状态由pending(等待)状态转换为fulfilled(成功)状态时的回调
    • reject函数:Promise对象状态由pending(等待)状态转换为rejected(失败)状态时的回调

    如果调用resolve函数与reject函数是带有参数那么这些参数会被传给回调函数(then或者是catch的参数),reject函数的参数通常是Error对象的实例,表示抛出的错误;resolve的参数除了正常的值外,还可以是另一个Promise实例,表示异步操作的结果可以是一个值还可以是一个异步操作(promise实例)。例如:

    var p1 = new Promise(function (resolve, reject) {
        setTimeout(() => reject(new Error("reject Error")), 3000);
    });
    
    var p2 = new Promise(function (resolve, reject) {
        setTimeout(() => resolve(p1), 1000);
    });
    
    p2.then(result => {console.log(result)});
    p2.catch(result => {console.log(result)});

    上面代码中p1和p2都是Promise实例。但是p2的resolve的参数是p1,即一个异步操作的结果返回的是另一个异步操作,注意此时p1的状态会传递给p2,也就是说,p1的状态决定了p2的状态。如果p1的状态是pending,那么p2的回调函数就会等待p1的状态改变;如果p1的状态已经是resolve或reject,p2将会立即执行。上面代码中,p1这个promise,在3秒后变成Rejected,p2的状态有p1决定,1秒之后p2调用resolve方式,但是此时p1的状态还么有改变,因此p2的状态也不会改变。又过了2秒。p1变成了Rejected,p2也跟着变成了Rejected。

    Promise对象的实例方法

    在JavaScript中,实例方法是指定在原型链prototype上的方法,Promise对象中有两个实列方法(原型方法):Promise.prototype.thenPromise.prototype.catch

    Promise.prototype.then:

    通过new创建Promise对象实例后,为了设置其值在resolve(成功) / reject(失败)时调用的回调函数,可以使用promise.then()实例方法进行设置,设置格式为:

    promise.then(onFulfilled, onRejected)
    • resolve(成功)时,onFulfilled方法会被调用
    • reject(失败)时,onRejected方法会被调用
    promise.then(function(result) {
        // 操作成功
    }, function(err) {
        // 操作失败
    });

    promise.then方法中,onFulfilledonRejected 两个都是可选参数,如果只用于对异常的处理,可以使用promise.then(undefined, onRejected)方式(即:只指定reject函数)。只处理异常时,也可以使用promise.catch方法。

    then方法返回一个新的Promise实例(注意不是原来的),因此常采用链式写法,即then后面再调用另一个then方法。

    // getJson方法返回promise对象
    getJson("/post.json").then(json => {
        return json.post;
    }).then(post => {
        console.log(post);
    });

    上面代码中使用then方法指定了两个回调函数,第一个回调函数完成之后,会将返回结果作为参数传入第二个回调函数。采用链式的then可以指定一组按照次序调用的回调函数。这时前一个回调函数有可能返回的还是一个promise实例(即可以返回promise实例也可以返回其他值),而后一个回调函数就会等待该Promise对象的状态发生变化,在被调用。

    getJson("/post.json").then(json => {
        return getJson("/po.json");
    }).then(post => {
        console.log(post);
    }, err => {
        console.log(err)
    });

    Promise.prototype.catch:

    方法使用格式如下:

    promise.catch(onRejected)

    Promise.prototype.catch方法是Promise.prototype.then(null, rejected)的别名,用于指定发生错误时的回调函数。

    getJson("/post.json").then(json => {
        ...
    }).catch(err => {
        console.log(err)
    });
    getJson方法返回一个promise对象,如果异步操作的状态是Resolved,则会调用then方法指定的回调函数;如果是Rejected状态则会调用catch方法指定的回调函数进行处理。
    如果Promise的状态变成Resolved,再抛出错误是无效的。另外Promise的错误具有“冒泡”特性,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获。
    getJson("/post.json").then(json => {
        return getJson("/po.json");
    }).then(post => {
    // some code console.log(post); }).
    catch(err => { console.log(err); });

    上面代码中,一共有3个Promise对象:一个有getJson产生,两个有then产生,其中任何一个抛出错误都会被最后一个catch捕获。

    为了提高代码的可读性,我们可以使用promise.then处理操作成功的情况,使用promise.catch处理操作失败的情况,示例如下:

    promise.then(function(result) {
        // 操作成功
    }).catch(function(err) {
        // 操作失败
    });

    如果没有使用catch指定回调函数来处理错误,Promise对象抛出的错误不会传递到外层代码。

    如果在catch中抛出了错误,或者是其他的为被捕获的Promise对象的错误,在Node.js中我们可以使用unhandledRejection事件来进行异常捕获。如下:

    var someAsyncThing = function () {
        var promise = new Promise((resolve, reject) => {
            resolve( X + 2);
        });
    
        return promise;
    };
    
    process.on("unhandledRejection", function (err, p) {
        console.log(err);
    });
    
    someAsyncThing().then(value => {console.log("everything ok")});

    其中unhandledRejection事件的监听函数有两个参数,第一个是错误对象,第二个是报错的Promise实例。

    Promise对象的静态方法

    静态方法又称为类方法,是指不需要实例化就可以使用的方法。在Promise对象还有一些静态方法,这些方法都是一些对Promise进行操作的辅助方法。

     Promise.all():

    • Promise.all(promiseArray)
    • 参数:promiseArray,是一个promise实例数组,数组中都是Promise实例,如果不是就会现调用Promise.resolve()将参数转成Promise实例,然后在进一步处理。
    • 方法作用:将多个Promise实例包装,生成并返回一个新的promise实例。参数传递promise数组中所有的promise实例都变为resolve的时候,该方法才会返回, 新创建的promise则会使用这些promise的值。如果参数中的任何一个promise为reject的话,则整个Promise.all调用会立即终止,并返回一个reject的新的promise对象
    var p1 = Promise.resolve(1),
        p2 = Promise.resolve(2),
        p3 = Promise.resolve(3);
    Promise.all([p1, p2, p3]).then(function (results) {
        console.log(results);  // [1, 2, 3]
    });

     Promise.race():

    • Promise.race(promiseArray)
    • 参数:promiseArray,是一个promise实例数组
    • 方法作用:将多个Promise实例包装,生成并返回一个新的promise实例。参数promise数组中的任何一个promise实例如果变为resolve或者reject的话,该函数就会返回,并使用这个promise实例的值进行resolve或者reject。
    var p1 = Promise.resolve(1),
        p2 = Promise.resolve(2),
        p3 = Promise.resolve(3);
    Promise.race([p1, p2, p3]).then(function (value) {
        console.log(value);  // 1
    });

    Promise.resolve():

    • Promise.resolve(obj)
    • 参数:obj,是一个待转换为promise的对象,其可能是以下3种形式:
      • promise实例是,原样返回
      • thenable对象是,将对象转换为promise对象并返回,这个对象具有then方法
      • 其它类型(包括JavaScript对或null等),返回一个将该对象作为值的新promise对象
    • 方法作用:将对象转为promise实例。参数promise数组中的任何一个promise实例如果变为resolve或者reject的话,该函数就会返回,并使用这个promise实例的值进行resolve或者reject。
    var p = Promise.resolve('Hello');
    
    p.then(function (s){
      console.log(s);   //Hello
    });

    Promise.reject():

    • Promise.reject(obj)
    • 参数:obj,是一个待转换为promise的对象
    • 方法作用:将对象转为promise实例,该实例的状态为rejected。
    var p = Promise.resolve('出错了');
    
    p.catch(function (s){
      console.log(s);   //出错了
    });

    Promise.resolve方法与thenable对象

    一般情况下我们会使用new Promise()来创建Promise对象实例。除使用构造函数外,我们还可以使用Promise.resolvePromise.reject两个静态方法。静态方法Promise.resolve(value)可以认为是 new Promise()方法的快捷方式,这方法可以将thenable对象转换为Promise对象。在ES6 Promisethenable对象是指一个拥有then方法的对象,且这个对象的then方法和Promise对象的then方法具有相同的功能和处理过程。

    var p = Promise.reject("error test");
    p.catch(err => {console.log(err)});

    Promise对象的使用

    使用Promise处理回调函数,可以使代码比较简洁和易读,对于顺序执行的多个回调,可以使用Promise方法链式调用,即:使用重复多个then的方法进行处理。

    下面我们用Promise对多个HTTP请示进行封装,并在最后一个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 = {
        nodejs: function () {
            return getURL('https://nodejs.org/api/index.json').then(JSON.parse);
        },
        github: function () {
            return getURL('https://api.github.com').then(JSON.parse);
        }
    };
    
    //保存结果
    function requestResults(results, value) {
        results.push(value);
        return results;
    }
    // [] 用来保结果的数组
    var pushResult = requestResults.bind(null, []);
    
    //请求数据
    request.nodejs()
    .then(pushResult)
    .then(request.github)
    .then(pushResult)
    .then(function(results){
        console.log(results);
    })
    .catch(function(err){
        console.log(err);
    });

    在上面代码中,.then(pushResult)方法用于将多个操作中的请求结果进行保存,并在所有结果回调至最后一个then方法中。在链方法处理过程中,任意一个then出现错误,都要结束其后的then方法,并将错误回调至最后的catch方法中。

  • 相关阅读:
    android动画坐标定义
    Android animation 动画背景图自动播放的实现
    松开手,你可以拥有更多
    Serializable继承和用途
    程序员必备的国外IT网站
    android 解析json数据格式
    免费的天气预报API谷歌,雅虎,中央气象台
    关于Lockfree Hash Table的一些链接资料
    字符串匹配算法之Brute force algorithm
    java 处理高精度计算
  • 原文地址:https://www.cnblogs.com/duhuo/p/4978863.html
Copyright © 2011-2022 走看看