使用ECMAScript 6 的Promise对象实现JavaScript深度嵌套回调的顺序链式调用

Promise是ES 6中新增的对象,Promise本质上是一种对异步处理的规范。Promise非常适合处理JS深度嵌套回调的问题,通过Promise处理回调可以使代码更简洁也更易读。主流浏览器(IE除外)的最新版本,已经提供了对ECMAScript 6的支持。Node.jsV 4.0 起,开始原生支持ECMAScript 6。所以,现在使用Promise已不需要额外引用包或模块,本文将分别介绍在浏览器中和在Node.js中使用Promise的链式调用。


1. Promise简单回顾

本站曾整理过几篇关于Promise介绍的文章。再简单回顾一下:创建一个Promise对象使用构造函数new Promise(),该函数接受一个函数参数,该函数包含两个参数:resolvereject,这两个参数是两个回调函数,分别用于执行成功和执行失败时回调。

var promise = new Promise(function(resolve, reject) {
    if (true){
        //操作成功时调用
        resolve(value);
    } else {
        //操作失败时调用
        reject(error);
    }
});

Promise包含两个实例方法:thencatch


  • then方法可以处理正常和异常两种情况,它有两个参数(函数参数)。第一个函数参数用于处理操作成功的情况,第二个函数参数用于处理操作失败的情况。


  • catch方法只能异常情况,它一个参数(函数参数),该参数用于处理操作失败的情况。

因此,对于操作成功和操作失败的处理,可以使用以下两种形式。

只使用then方法,处理操作成功和操作失败的情况:

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

使用then方法处理操作成功的情况,使用catch方法处理操作失败的情况:

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


Promise实现链式调用

then方法也可以接受另一个Promise实例的传入,或thenable对象(包含then的对象或方法)。因此,在前一步的then方法中,只要在操作成功中返回一个Promise实例或thenable对象,就可以实现类似then().then().then().catch()形式的链式调用。这样只需要在每一个then()处理正常情况,只要在最后的catch()方法中处理异常情况即可。

示例如下:

promise.then(function(result){
    //上一步操作成功
    console.log(result);
    //返回另一个thenable对象
    return new Promise(……);
}).then(function(result){
    //上一步操作成功
    console.log(result);
    //返回另一个thenable对象
    return new Promise(……);
}).then(function(result){
    //上一步操作成功
    console.log(result);
    //返回另一个thenable对象
    return new Promise(……);
}).catch(function(error){
     //处理异常
     console.log(error);
});


2. 在浏览器中使用Promise的链式调用

现在有getURL方法,该使用XMLHttpRequest进行HTTP请求,该方法的返回值是一个Promise对象实例。下面使用这个方法以链式调用的形式分别请求两个网址:

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();
    });
}

getURL('https://api.github.com').then(function(result){
    //第一次请求的结果
    console.log(result);
    return getURL('https://nodejs.org/api/index.json');
}).then(function(result){
    //第二次请求的结果
    console.log(result);
}).catch(function(error){
    //请求失败
    console.log(error);
});


3. 在Node.js中使用Promise的链式调用

现有readFile方法,该方法使用fs模块读取文件,其返回值是一个Promise对象实例。下面使用这个方法以链式调用的形式,分别读取a.txtb.txt两个文件:

var fs = require('fs');

function readFile(file){
    return new Promise(function(resolve, reject){
        fs.readFile(file, function(err, result){
	    if(err){
		reject(err);
	    } else {
		resolve(result);
	    }
	});
    });
}

readFile('./a.txt').then(function(result){
    //显示第一个文件的读取结果
    console.log(result.toString());
    //返回另一个Promise对象实例
    return readFile('./b.txt');
}).then(function(result){
    //显示第二个文件的读取结果
    console.log(result.toString());
}).catch(function(error){
    //文件读取失败
    console.log(error);
});

                       

http://niefengjun.cn/javascript/js/41TziMm-x.html