Promise对象探究

原创 2016年08月15日 12:22:42

一、简介


Promises对象是CommonJS工作组提出的一种规范,目的是为异步编程提供统一接口。

以上定义引自阮一峰《Javascript异步编程的4种方法》。

我的简单理解是,Promise是JavaScript异步回调的解决方案之一,最大的优点是以链式调用的方式来消除Callback Hell的问题。


二、用法


1、运用一个Promise对象可以总结成两个步骤:

  1. 异步。决定回调的时机(when)。
  2. 回调。实现回调的内容(what)。

举个栗子,通常网络模块的业务逻辑是:请求数据、解析返回的数据并显示。为了代码写起来简单一点,这里抽象成“进行耗时操作(比如timeout个3秒)、显示数据”。
在这里,“异步”就是耗时操作的过程:

// 定义一个Promise对象,通过new Promise的方式
// 参数是待定的。resolveCallback是成功的回调,rejectCallback是失败的回调
// resolveCallback和rejectCallback,只有其中一个会被执行

var promise = new Promise(function (resolveCallback, rejectCallback) {
    setTimeout(function () {
        resolveCallback("this is the returned data.");
    }, 3000);
});

“回调”就是之后的把数据显示在View上的过程:

// 用then,参数是两个回调函数的实现:一个成功的回调,一个失败的回调

promise
    .then(function (data) {
        console.log(data); // 成功的具体的回调,把data显示在界面上
    }, function (error) {
        console.log(error); // 失败的具体回调
    })


2、优点:链式调用

因为每次使用then,会返回Promise对象本身,而这个Promise对象又有then方法,所以可以一直调用下去,称之为“链式调用”。这也是Promise最大的优点,我们通常说它“更优雅的异步回调”,是因为它解决了Callback Hell的问题。链式调用最关键的地方在于,后一个then的回调参数,是前一个then的回调中的返回值,就像这样:

// 异步过程
var promise = new Promise(function (resolveCallback, rejectCallback) {
    setTimeout(function () {
        resolveCallback("this is the returned data.");
    }, 2000);
});

// 回调过程
promise

    .then(function (data) {
        console.log(data);
        return data + "again."; //return是给下一个then的参数。缺少就是undefined
    }, function (error) {
        console.log(error);
    })

    .then(function (data) { // 这里接收到上一个then给的参数
        console.log(data);
    }, function (error) {
        console.log(error);
    });
    // 可以无限then下去……

结果截图:
这里写图片描述


三、对比


1、与Android中接口回调的联系:

上面说到的两个步骤,“异步”和“回调”,很容易让人联想到Android中类似的做法。在Android SDK代码中,如果想要进行网球请求的异步回调,通常的做法是定义一个interface来作为回调的媒介,在这个接口中,至少含有请求成功、失败的回调。

// 定义网络请求回调的统一接口

public interface NetworkCallback {

    void onSuccess(String response); //成功的回调,待实现

    void onError(String error);      //失败的回调,待实现
}

注意,以下代码是为了简便而简写,可能有错误的地方,比如一些try catch就暂时省略。

// 异步请求。决定回调的时机。
// 假定该文件是NetworkHelper.class

public void get(String url, NetworkCallback cb) {
    HttpUrlConnection conn = (HttpUrlConnection) new URL(url).openConnection();
    if (conn.getStatusCode() == 200) { // 如果请求OK
        String data;
        //...get data from InputStream
        cb.onSuccess(data);     // 回调成功的函数
    } else {
        cb.onError("Error..."); // 回调失败的函数
    }
}
// 回调。实现回调的内容,也就是实现上面定义的接口。

NetworkHelper.get("http://www.google.com", new NetworkCallback() {

    @Override
    public void onSuccess(String response) {
        // 处理、显示返回的结果
    }

    @Override
    public void onError(String error) {
        // 处理错误
    }
});

可见,Android中的异步回调的这种做法,跟Promise的使用过程是很相似的,因为思想是一样的,就是异步+回调。不过,Promise自身多了一个链式调用的优点。


2、与NodeJS中经典的异步API的比较:

NodeJS本身提供了许多异步的“错误优先”的API,比如一个读取文件的函数readFile,假如我想要读取文件完毕之后,打印出一句话“Done!”,那一般的做法是:

var fs = require('fs');

function read(uri) {
    fs.readFile(uri, function (err, data) {
        if (!err) {
            console.log('Done!');
            // 这里可能还会有更多异步操作,就会造成Callback Hell
        } else {

        }
    });
}

为了避免Callback Hell,我们可以把它封装成一个Promise对象:

var fs = require('fs');

function read(uri) {
    return new Promise(function (resolve, reject) {

        fs.readFile(uri, function (err, data) {
            if (err) {
                reject(err);
            } else {
                resolve(data);
            }
        });

    });
}

read('./../images/test.png')

    .then(function (data) {
        console.log('length of file1:', data.toString().length);
        return read('./hello.js');  // 这里返回一个promise对象以链式调用
    }, function (err) {
        console.log(err);
    })

    .then(function (data) {
        console.log('length of file2:', data.toString().length)
    }, function (err) {
        console.log(err);
    });

效果:
这里写图片描述

如果想要深入了解链式调用的封装效果,推荐这篇博客:http://swiftcafe.io/2016/07/27/updator/


3、与RxJava(RxAndroid)的类比:

把嵌套回调写成链式,除了Promise,还有RxJava。不过在这里就不详细展开了,就大致列一下它们之间相同和不同的地方吧。

相同点:
  • 链式解决Callback Hell。
不同点:
  • RxJava是面向数据流的,将数据封装成特有的类型“Observable”;并且,提供对Observable的一系列便捷的操作,即函数式编程。而Promise没有对数据的更进一步的封装。
  • RxJava可以方便地在线程之间切换,实现更高权限的控制。
版权声明:本文为博主原创文章,未经博主允许不得转载。

【javascript笔记】Promise对象

最初的异步流程控制,是通过回调来实现,但是存在回调太多层就会让人崩溃后来出现了,异步解决方案 async 可以应用到NodeJS 和 browser里面。后来ES6 将 promise 引入标准, ...

ES6学习笔记(七)--Generator函数与Promise对象

Generator函数执行Generator函数会返回一个遍历器对象,也就是说,Generator函数除了状态机,还是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历Generator函数内部的...
  • SirM2z
  • SirM2z
  • 2016年06月01日 17:18
  • 1461

AngularJS中的$http、$q服务和promise对象的联系

一、介绍:$http服务是AngularJS系统自带的,可以用来进行网络通信、获取远程服务器的数据。要记住的是,$http是对浏览器XMLHttpRequest的封装,也就是说,它其实是Ajax。首先...

Promise 对象的使用方法

Promise对象的意义:说一个浅显的例子,在日常的开发中,在一次异步请求成功之后,拿到请求到的参数,再进行往下的n个操作,有的时候或许还能用到异步请求之后再接下来一个异步请求,下面代码是最直接无脑的...

谈谈 ES6 的 Promise 对象

前言 开篇首先设想一个日常开发常常会遇到的需求:在多个接口异步请求数据,然后利用这些数据来进行一系列的操作。一般会这样去写: $.ajax({ url: '......', su...

第十五节,Promise对象

Promise对象 Promise的含义 基本用法 Promise.prototype.then() Promise.prototype.catch() Promise.all() ...

使用jQuery Deferred对象实现Promise

Making Promises With jQuery Deferred 本Markdown编辑器使用StackEdit修改而来,用它写博客,将会带来全新的体验哦: Markdown和扩展Mar...
  • bdss58
  • bdss58
  • 2015年12月20日 00:43
  • 751

es6 javascript的Promise对象(下)

5 Promise.all() Promise.all方法用于将多个 Promise 实例, 包装成一个新的 Promise 实例。 var p = Promise.all([p1, p2, p3...

Angularjs promise对象解析

转载自cnblogs----->点击打开链接         1、先来看一段Demo,看完这个demo你可以思考下如果使用$.ajax如何处理同样的逻辑,使用ng的promise有何优势? ...

ES6 Promise 对象

前言 开篇首先设想一个日常开发常常会遇到的需求:在多个接口异步请求数据,然后利用这些数据来进行一系列的操作。一般会这样去写: $.ajax({ url: '......', succ...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Promise对象探究
举报原因:
原因补充:

(最多只允许输入30个字)