从回调地狱谈Promise

碎碎念

本质上,Promise 是一个绑定了回调的对象,而不是将回调传进函数内部。它代表了一个异步操作的最终完成或者失败。

node读取文件

我们知道readFile API是异步操作,如果我们想依次读取文件的话,需要嵌入

const fs = require('fs')
const path = require('path')
function readTxt(dir,callback){
    fs.readFile(path.join(__dirname,dir),'utf8',(err,data)=>{
        if(err) throw err;
        callback(data)
    })
}
readTxt('01.txt',function (data) {
    console.log(data)
    readTxt('02.txt',function (data) {
        console.log(data)
        readTxt('03.txt',function (data) {
            console.log(data)
        })
    })
})

这种代码可读性不强,而且如果阅读更多文件的话,就是回调地狱,十分混乱,那怎样解决这个异步操作?如果使用return,那么后续的代码不会继续执行;而Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。

了解Promise

所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。–阮一峰

Promise对象两个特点
  1. 对象的状态不受外界的影响。Promise代表一个异步操作,有三种状态:(1)pending进行中(2)fufilled已成功(代码中一般参数用resolved)(3)rejected已失败。只有异步操作的结果,可以决定是哪种状态;任何其它操作到无法改变这个状态;这也是Promise名字的由来;它的英语意思是“承诺”,意思是任何手段都改变不了
  2. 一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象状态的改变,只有两种可能;一种是pendingfufilled;一种是pendingrejected;只要这两种情况发生,状态就已经凝固,不会再变了,会一直保持这个结果。这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。

使用Promise上面的回调地狱

有了 Promise 对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise 对象提供统一的接口,使得控制异步操作更加容易。

如何使用
  1. Promise是一个构造函数,既然是一个构造函数,那么可以使用new Promise()得到一个实例
  2. 在Promise上分别有两个函数,一个是resolve(成功之后的回调);一个是reject(失败之后的回调函数)
  3. 在Promise的构造函数的prototype原型上,有一个then()方法,只要是Promise的实例对象都可以访问到·
var promise = new Promise(function(resolve, reject) {
 if (/* 异步操作成功 */){
 resolve(value);
 } else {
 reject(error);
 }
});

promise.then(function(value) {
 // success
}, function(value) {
 // failure
});
解决文件的层层嵌套
const fs = require('fs')
const path = require('path')
function readTxt(dir){
    return new Promise(function(resolve,reject){
        fs.readFile(path.join(__dirname,dir),'utf8',(err,data)=>{
            if(err){
                // pending到fufilled,状态已经凝固不会再发生改变,会一直保持这个结果
                reject(err)
            }else{
                resolve(data)
            }
        })
    })
}
readTxt('01.txt')
    .then(function (data) {
    console.log(data)
    return readTxt('02.txt')
    },function (err) {
        console.log(err)
    // 采用箭头函数
    })
    .then((data)=>{
        console.log(data)
        return readTxt('03.txt')
    })
    .then((data)=>{
        console.log(data)
    })
    //如果前面有任何Promise失败,则立即终止执行,并马上进入catch处理Promise抛出异常
    .catch((err)=>{
        throw err;
    })

打印的结果一直不变

描述和方法

Promise是一个代理对象(代理一个值),被代理的值在Promise可能是未知的;它允许你为异步操作的成功和失败分别绑定相应的处理方法;这让异步操作可以像同步方法那样返回值;但不是立即返回执行的结果;而是一个能代表未来出现的结果的Promise对象

pending状态的方法的Primise对象可能会变fulfilled 状态并传递一个值给相应的状态处理方法,也可能变为失败状态(rejected)并传递失败信息。当其中任一种情况出现时,Promise 对象的 then 方法绑定的处理方法(handlers )就会被调用(then方法包含两个参数:onfulfilled 和 onrejected,它们都是 Function 类型。当Promise状态为fulfilled时,调用 then 的 onfulfilled 方法,当Promise状态为rejected时,调用 then 的 onrejected 方法, 所以在异步操作的完成和绑定处理方法之间不存在竞争)。

使用setTime来模拟

let myFirstPromise = new Promise(function(resolve, reject){
    //当异步代码执行成功时,我们才会调用resolve(...), 当异步代码失败时就会调用reject(...)
    //在本例中,我们使用setTimeout(...)来模拟异步代码,实际编码时可能是XHR请求或是HTML5的一些API方法.
    setTimeout(function(){
        resolve("成功!"); //代码正常执行!
    }, 250);
});

myFirstPromise.then(function(successMessage){
    //successMessage的值是上面调用resolve(...)方法传入的值.
    //successMessage参数不一定非要是字符串类型,这里只是举个例子
    console.log("Yay! " + successMessage);
});
原型上的API

Promise.prototype.catch(onRejected)
添加一个拒绝(rejection) 回调到当前 promise, 返回一个新的promise。一般来说作用是:如果有任何的 Promise 执行失败,则立即终止所有 promise 的执行,并 马上进入 catch 去处理 Promise中 抛出的异常
Promise.prototype.then(onFulfilled, onRejected)
添加解决(fulfillment)和拒绝(rejection)回调到当前 promise, 返回一个新的 promise, 将以回调的返回值来resolve.
Promise.prototype.finally(onFinally)
添加一个事件处理回调于当前promise对象,并且在原promise对象解析完毕后,返回一个新的promise对象。回调会在当前promise运行完毕后被调用,无论当前promise的状态是完成(fulfilled)还是失败(rejected)

让函数具有promise功能

我们平时用到的时候,一般是异步请求用得到,所以我们使用XML为例子

  1. 使用node核心服务http模块来搭建web服务器(这里我们使用express框架来搭建)
cnpm init -y
cnpm i express body-parser -S
  1. 后台服务器
const express = require('express');
const app = express();
const bodyParser = require('body-parser')
// 挂载内置中间件
app.use(express.static('public'));
// 挂载参数处理中间件(post)
app.use(bodyParser.urlencoded({ extended: false }));
// 处理json格式的参数
app.use(bodyParser.json());
// 处理get提交参数
app.get('/login',(req,res)=>{
    res.send('get data');
});
app.get('/register',(req,res)=>{
        res.send('register');
});

app.listen(3000,()=>{
    console.log('running...');
});

启动我们的服务器
3. 静态资源(新建public文件夹,在public新建05login.html)

<button>请求服务端数据</button>
<script>
let btn = document.querySelector('button')
btn.onclick = function () {
    const xhr = new XMLHttpRequest()
    xhr.open('get', 'http://localhost:3000/login');
    xhr.onload = function (data) {
        console.log(data.currentTarget.responseText)
    };
    xhr.send();
}
</script>

打开login.html页面
4. 让异步请求拥有promise的功能,只需要返回一个promise即可

function myAsyncFunction(url){
    return new Promise((resolve,reject)=>{
        let xhr = new XMLHttpRequest()
        xhr.open('get',`http://localhost:3000${url}`)
        xhr.onload = () => resolve(xhr.responseText)
        xhr.onerror = () => reject(xhr.statusText)
        xhr.send()
    })
}
btn.onclick = function () {
    myAsyncFunction('/login')
        .then(function (data) {
            console.log(data)
        })
        .catch(function (err) {
            throw err;
        })
}

请求node数据

参考文档

Promise 对象 阮一峰
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值