Promise对象探究

    xiaoxiao2025-07-28  5

    一、简介


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

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

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

    二、用法


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

    异步。决定回调的时机(when)。回调。实现回调的内容(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可以方便地在线程之间切换,实现更高权限的控制。
    转载请注明原文地址: https://ju.6miu.com/read-1301163.html
    最新回复(0)