看这一篇就够了!浅谈ES6的Promise对象

前言

了解前端开发的同学都知道,在浏览器中javascript主线程是单线程的。试想一下, 多个线程操作同一个dom,那岂不是很混乱?并且单线程可以节省资源和便于对上下文context的控制。也正是因为单线程的原因,js中处理异步调用的问题多数采用了回调函数来作为解决方案。于是乎便产生js开发过程经常遇到的一个经典问题 回调地狱

1.何为回调地狱

相信大家会经常遇到这样的场景:

  • 表单的查询条件 需要ajax调用后台API来渲染
  • 列表查询需要等待表单查询条件渲染完成后,根据其中的某一个值来发起ajax调用来获取后台数据
  • ...
$.ajax({                                        //第一次调用
    url: '/api_1',
    type: 'post',
    data: {...},
    success(data){
        $.ajax({                                //第二次调用
            url: '/api_2',
            type: 'post',
            data: {...}
            success(data){
                $.ajax({                        //第三次调用
                    url: '/api_3',
                    type: 'post',
                    data: {...}
                    success(data){
                        
                    }
                })
            }
        })
    }
})
复制代码

这样的编码方式就是所谓的回调地狱, 嵌套层次太多,不便于代码的阅读。而且对于日后代码的重构也是在挖坑。

Promise 正是为解决此问题而产生的,它已经成为ES6标准的一部分。

2. 神马是 Promise

面试的时候经常会被问到,promise是什么。可能你已经用过,但未必能真正说清楚。 简单来说:

1. Promise 是一种异步编程的解决方案。
2. Promise 本质类似数据库中的事物:有状态
3. Promise 的状态分为三种: pending(阻塞), fulfilled(成功或者完成), rejected(失败) 
复制代码

到这里你可能又说了,这些概念到处都有,能不能说点人话, 用自然语言来描述。想必面试的时候能够用自己的话,讲复杂的概念讲清楚的,也算是高人一个了。

2.1 Promise 来源于生活

2.1.1 初识Promise

在一个浪漫的夜晚, 你对暗恋了多年的女生表白。说我要给你一个承诺 ( Promise ), 这个时候你在等待你女神的回复 ( pending状态 ) , 你的女神也正好喜欢你,给你一个肯定的回答, 说我们在一起吧 ( fulfilled 状态 ) , 亦或者女神已经有喜欢的人,那只能拒绝你了 ( rejected 状态 )。而且大家都是好面子的人,只会表白一次。女神再给你回复后,不会再说第二次。也就是说 。只要这两种情况中的一种发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。 等到女神的回复后或兴奋亦或桑心。你会做出不同的反应 调用 then 方法

演示过程:

  • 代码片段1:
let p = new Promise((resolve, reject) => {
    resolve('兴奋');
});

p.then((success) =>{
    console.log(success);
},(err) => {
    console.log( err );
});
复制代码

  • 代码片段2
let p = new Promise((resolve, reject) => {
   reject('表白失败了');
});

p.then((success) =>{
    console.log(success);
},(err) => {
    console.log( err );
});
复制代码

  • 代码片段3

这里你的女神就不是女神了,而是女神经了吧。哈哈,你也不会任由她逗你玩。得到一个回复后,不再相信你女神了。

let p = new Promise((resolve, reject) => {
    resolve('兴奋'); //这里只会处理一个反馈。
    reject('表白失败了');  
});

p.then((success) =>{
    console.log(success);
},(err) => {
    console.log( err );
});
复制代码

2.1.2 Promise中的异步调用

可故事总是峰回路转的,女神在给你答复前, 说你等等 ( 异步调用 )


let p = new Promise((resolve, reject) => {
   setTimeout(()=>{   //同样可以用作ajax的异步回调中
     resolve('兴奋');
   }, 1000);
});

p.then((success) => {
    console.log(success);
}, (err) => {
    console.log(err);
});
复制代码

很多时候,女神都是比较害羞的。一般都会需要考虑考虑。说容我想几天再答复你吧。

2.1.3 链式调用(干掉回调地狱)

当获得女神的芳心后,经过一段时间的相处,女神会把你介绍她的父母。这个时候你就要好好捯饬捯饬了。一步一步(链式调用 p.then1.then2...)走向人生的巅峰。

  • 代码演示

想让女神家长认识你,就先得认识女神,从女神那里获得认识女神家长的通关密码。知道女神家长都喜欢啥。哈哈哈~

假设: key1.txt文件中 存放着 key2.txt key2.txt文件中 存放着 小伙子,你很不错哦~

let fs = require('fs');
function getKey(){
    fs.readFile('key1.txt', 'utf8', (err, data) => {
        if(err){
            console.log(err);
            return;
        }
        
        console.log(data);  //key2.txt
        
        fs.readFile(data, 'utf8', (err, data) => {
            if(err){
                console.log(err);
                return;
            }
            
            console.log(data);  //小伙子你很不错哦~
        })
        
    })
}
复制代码

我们都知道readFile是一个异步调用的过程。如果想拿到上一步的结果,那就只能等待上一个执行结果完成后,再在回调函数了嵌套下一个执行函数。

改用promise 如何来实现呢?

function getKey(params){
    return new Promise((resolve, reject) => {
        fs.readFile(params, 'utf8', (err, data) => {
            if(err){
                reject(err);
            }
            
            resolve(data);
        })
    });
}

getKey('key1.txt').then((data) => {
    return  getKey(data);
}).then((data)=>{
    console.log(data);  //小伙子你很不错哦~
})
复制代码

当第一个then中返回一个promise,会将返回的promise的结果,传递到下一个then中。这就是比较著名的链式调用了。

从代码风格上来看,也显得更加直观了。

2.1.4 统一的错误处理函数 catch

在上面的代码中,then 函数中都只传了一个用来解析 resovle 状态的函数。那如果 readFile发生错误了。该如何处理呢?

getKey('key1.txt').then((data) => {
    return  getKey(data);
}).then((data)=>{
    console.log(data);  //小伙子你很不错哦~
}).
catch((err)=>{
    console.log(err);
})
复制代码

调用resolve或reject并不会终结 Promise 的参数函数的执行。

Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获。

Promise的实现过程

很多时候,为了赶项目进度,知其然,然不知其所以然。相信上面的知识你已经早就了然于胸。阮一峰的ES6基础教程也有更为详细的API介绍。

下面我们就一层一层的剥开Promise的神秘面纱。看看女神到底长什么样子。

  • 创建一个Promise类
class Promise{
    
}
复制代码
  • Promise 中存在一个构造函数。通过前面的代码,我们可以看到:

Promise 新建后就会立即执行, 也就是 new Promise();

class Promise{
    constructor(executor){
        
    }
}
复制代码
  • Promise 原型上还要有一个then函数
class Promise{
    constructor(executor){  //示例:executor:(resovle, reject) => { resolve('兴奋')  }, 初始化时,告诉promise, 什么状态调用什么函数
        
    }
    then(onFulFilled, onRejected){  //用来接收promise状态函数的返回结果
        
    }
}
复制代码
  • 有了基本的结构 Promise/A+ 规范中有这么一句话:

“value” is any legal JavaScript value (including undefined, a thenable, or a promise.

class Promise{
    constructor(executor){
        this.status = 'pending'; //默认是pending状态
        this.value = undefined;  //默认值
        this.reason = undefined;
        
        let resolve = (data) => {
            if(this.status === 'pending'){ //还记得女神经那段吧。哈哈哈~
                this.status = 'resolved';
                this.value  = data;
            }
        }
        
        let reject = (err) => {
            if(this.status === 'pending'){
                this.status = 'rejected';
                this.reason = err;
            }
        }
        
        try{ //上述是例中 readFile这样的函数执行过程中就有可能遇到错误
            executor(resolve, reject);
        }catch(e){
            reject(e);
        }
    }
    
    then(onFulFilled, onRejected){
        if(this.status === 'resolved'){
            onFulFilled(this.value);
        }
        if(this.status === 'rejected'){
            onRejected(this.reason);
        }
    }
}
复制代码

3. 小结

上面这段代码初探了Promise 内部实现。对于Promise 内部异步函数的调用,链式调用的实现 待续...., 欢迎批评指正。继续关注。

转载于:https://juejin.im/post/5b02246f5188251cee0c91ab

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值