前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ES6中的Promise和Fetch

ES6中的Promise和Fetch

作者头像
张子阳
发布2018-09-30 10:01:13
1.4K0
发布2018-09-30 10:01:13
举报

ES6中的Promise和Fetch

2018-1-24 作者: 张子阳 分类: Web前端

JavaScript是单线程执行的,因此,为了避免操作时的页面中断(体现为页面假死),可以使用回调函数。但是如果回调函数中仍然嵌套有回调函数,代码就会变得越来越不可维护。这篇文章介绍ES6如何通过Promise解决这个问题,并介绍了相关的Fetch方法。

回调地狱Callback hell

假设有一个服务接口,接受两个操作数,可以完成相加的操作,并以Json字符串返回结果。返回的结果结构如下:

代码语言:txt
复制
{
    Data: 3,                    // 计算结果
    IsSuccess: true,            // 是否成功
    Message: "",                // 错误消息
    State:0                     // 执行的结果状态
}

本例中仅需要关注Data,其他3个值总是正确的。

那么,当要完成1+2+3+4这个操作时,如果采用传统的方式,代码可能是这样的:

代码语言:txt
复制
function post(url, data, sucess, err){               
    var request = new XMLHttpRequest();
    
    request.onreadystatechange = function(){
        if(request.readyState == 4) {                       
            if(request.status == 200){
               sucess(request.responseText); 
            }else if(typeof err == "function"){
               err(request.status, request.responseText) 
            }
        }
    }
    
    request.open("POST", url, true);
    request.send(JSON.stringify(data))              
}


post("http://test.tracefact.net/compute/add", {x:1, y:2}, function(res){
    res = JSON.parse(res);
    var z = res.Data;      // 3             
    
    post("http://test.tracefact.net/compute/add", {x:z, y:3}, function(res){
        
        res = JSON.parse(res);
        var z = res.Data;  // 6
        
        
        post("http://test.tracefact.net/compute/add", {x:z, y:4}, function(res){
            res = JSON.parse(res);
            var z = res.Data;      // 10                        
            alert(z);
        });
    });         
});

http://test.tracefact.net/compute/add接口是真实存在并且可以被调用的。

可以看到,success回调函数调用了3次,嵌套了2层。简单起见,没有传入err回调函数。可以通过将url修改为错误的,或者将x传入字符串来制造错误,这里就不演示了。

使用Promise

ES6引入了Promise来解决这个问题,简单来说,Promise将一层套一层的的回调,改成链式操作。

Promise共有三种状态:pending(初始状态,既不是成功,也不是失败状态)、fulfilled( 意味着操作成功完成)、rejected(意味着操作失败)。

Promimse的构造函数接受一个函数,这个函数的两个参数分别称作resolve方法和reject方法。当任务成功时,调用resolve()方法,失败时,调用reject()方法。调用resolve和reject时,传入的值,将作为输入参数,传递到then方法的resolve和reject中。

在Promise对象上可以调用then()方法,它也接受两个方法,一个是resolve,一个是reject。then()方法返回的还是一个Promise对象,因此支持链式调用。值得注意的是:then方法中上一个resolve方法的返回值,将成为下一个then方法中resolve的输入参数。由此构成了数据的流动。

上面两段话如果不结合代码,很难理解清楚,我们继续看1+2+3+4这个例子,为了简单起见,先不使用post方法异步操作。

代码语言:txt
复制
var p = new Promise(function(resolve, reject){
    var x = 1, y =2;                
    resolve(x+y);       // 传入结果3
});

以上代码是立即执行的,仅仅完成了1+2。那么如何利用这个计算结果完成下一步+3的操作呢?可以通过then方法。

代码语言:txt
复制
var p = new Promise(function(resolve, reject){
    var x = 1, y =2;                
    resolve(x+y);
});

p.then(function(z){   // 这里z是上一步的计算结果3             
    var y = 3;    
    return z+y;       // 6
})

类似地,完成+4操作,只需要再then一次就可以了。

代码语言:txt
复制
p.then(function(z){             
    var y = 3;
    return z+y;   // 6
}).then(function(z){
    var y = 4;
    return z+y;   // 10
})

接下来,我们改下post的例子,将它变成Promise版本的。

代码语言:txt
复制
var p = new Promise(function(resolve, reject){
    var x = 1, y =2;                
    post("http://test.tracefact.net/compute/add", {x:1, y:2}, function(res){
        res = JSON.parse(res);
        console.log("step1:", res);
        resolve(res.Data);      // 3
    })
});

p.then(function(z){
    return new Promise(function(resolve, reject){
        post("http://test.tracefact.net/compute/add", {x:z, y:3}, function(res){
            res = JSON.parse(res);
            console.log("step2:", res);
            resolve(res.Data);      // 6
        });        
    });    
    
}).then(function(z){
    return new Promise(function(resolve, reject){
        post("http://test.tracefact.net/compute/add", {x:z, y:4}, function(res){
            res = JSON.parse(res);
            console.log("step3:", res);
            resolve(res.Data);      // 10
        });        
    });
})

reject和异常处理

resolve用来处理正常流程,reject则用来处理失败的情况,用法和resolve是类似的,例如下面,我们将x的参数改为“s”,服务端将会返回400 bad request,此时可以添加then的第二个参数reject进行处理:

代码语言:txt
复制
var p = new Promise(function(resolve, reject){
    var x = 1, y =2;                
    post("http://test.tracefact.net/compute/add", {x:"s", y:2}, function(res){
        res = JSON.parse(res);
        console.log("step1:", res);
        resolve(res.Data);      // 3
    }, function(state, msg){
        reject(state + " " + msg)
    })
});

p.then(function(z){
    return new Promise(function(resolve, reject){
        post("http://test.tracefact.net/compute/add", {x:z, y:3}, function(res){
            res = JSON.parse(res);
            console.log("step2: ", res);
            resolve(res.Data);      // 6
        });        
    });    
    
}, function(msg){
    alert("step2: " + msg)
});

除了使用reject,还有一种方式,就是使用catch方法,示例如下:

代码语言:txt
复制
p.then(function(z){
    return new Promise(function(resolve, reject){
        post("http://test.tracefact.net/compute/add", {x:z, y:3}, function(res){
            res = JSON.parse(res);
            console.log("step2: ", res);
            resolve(res.Data);      // 6
        });
    });    
}).catch(function(msg){
    alert("step2: " + msg)
});

一般来说,使用catch会更简单清晰一些。

fetch方法

在过去,因为缺乏统一的标准,发起ajax异步请求,在不同的浏览器下有不同的方式,主要是使用XMLHttpRequest对象和ActiveXObject("Msxml2.XMLHTTP")。在ES6中,提供了fetch方法简化了这一操作。除此以外,fetch方法返回的是一个Promise对象,因此,可以链式发起异步请求。而服务端的返回值则通过response对象传递。

再次改写上面的例子:

代码语言:txt
复制
fetch("http://test.tracefact.net/compute/add", {
    method:"POST",
    headers:{ "Content-Type": "application/json" },
    body: JSON.stringify({x:1, y:2})
}).then(function(res){   
    return res.json();
}).then(function(res){
    return fetch("http://test.tracefact.net/compute/add", {
        method:"POST",
        headers:{ "Content-Type": "application/json" },
        body: JSON.stringify({x:res.Data, y:3})
    })    
}).then(function(res){
    return res.json();
}).then(function(res){
    console.log(res.Data);    
}).catch(function(err){
    alert("err: " + err);
});

使用fetch时第一步then返回的response对象(res),和直接使用前面post方法返回的res并不是同一个对象。这个对象的详细内容可以参考这里:https://developer.mozilla.org/zh-CN/docs/Web/API/Response。在这个response上调用json()方法,返回的也是一个Promise,然后再下一步then才能够获得服务器返回的原始对象。

总结

这篇文章主要讲述了ES6中的Promise对象和Fetch方法,上面的代码,无需Babel就可以在新版本Chrome浏览器下直接运行,建议想要熟悉的朋友们敲一遍代码,执行一遍以加深理解。

感谢阅读,希望这篇文章能给你带来帮助!

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018-1-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • ES6中的Promise和Fetch
    • 回调地狱Callback hell
      • 使用Promise
        • reject和异常处理
          • fetch方法
            • 总结
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档