promis模式的javascript实现

本文介绍使用Promise模式简化JavaScript中复杂的异步任务处理。通过一个生动的例子——程序员求婚流程,展示如何运用Promise模式实现任务间的有序执行及状态传递。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

javascript语言是一门函数式编程的语言,回调函数的使用是常用的,特别是在异步编程时,回调函数会在异步函数完成之后调用。 但是在编程中会遇到这样一类问题,A任务完成之后 才可以开始B任务,B任务完成之后才可以执行C任务,C任务完成后,才能执行D任务。假设ABCD四个任务都是同步的,则应该有下面代码:


task("A").done();
task("B").done();
task("C").done();
task("D").done();

假设ABCD四个任务不全是同步的,最极端的情况,四个都是异步的,则应该有下面回调嵌套式的代码:

task("A").done(function(){
    task("B").done(function(){
        task("C").done(function(){
            task("D").done();
        });
    });
});

但是这样的有个比较明显的问题,假设不是四个任务,而是10个8个,按照上面的写法,肯定会横向拉得很长。再加上可能会有其他逻辑添加到回调中去,对整个代码的维护是极为不利的。

那么有什么比较好的方式,可以解决这个问题。其实对于函数式编程语言,很早就有一种名为promis的设计模式,就是专门用来解决回调函数嵌套问题的。如果使用promis模式,上述嵌套代码可以写成如下链式结构:

Deffered(task("A"))
        .then(task("B"))
        .then(task("C"))
        .then(task("D"));

这样即便有再多的任务,都可以竖着排下去,逻辑上也可以解套,代码上也简单明了,比之上面的嵌套模式,要好太多。

下面用一个比较活泼的例子,来实现以下promis模式。

有这样一个问题:

程序员小王想娶女神小米。

小米说必须先让她爸爸认可,然后让妈妈认可,然后让大伯认可,且让妈妈认可的前提是爸爸认可,让大伯认可的前提是爸爸妈妈都认可,然后我在考虑~

假设:他们各个人同意的可能性都为80%,而且都必须有一段时间考虑,每个人可以请求两次。

问:小王能不能取到小米~?

使用程序实现以上问题。

按照面向对象的思维,分析以上问题,小王对每个人得请求方式都是一样的,所以可以把这个过程封装到一个Request类中。

这个类至少有以下方法:

aks:        问是否可以娶小米

answer: 答 可以 或者 不可以

fail :        如果请求失败了,就执行此方法

done:    如果最终请求成功了,就执行此方法

begin:      开始执行请求方法

下面是我对这个类的实现:

(function(win){

    function Request(name){
        if(!(this instanceof Request)){
            return new Request(name);
        }else {
            var p = Promis.create(this);
            this.name = name;
            this.promis = p;
        }
    }

    Request.prototype={
        promis:null,
        name:””,
        _isfirst:true,
        begin:function(){
            this.ask();
            this.answer();
        },
        fail:function(){
            alert(“求婚失败”);
        },
        done:function(){
            alert(“求婚成功”);
        },
        ask:function(){
            var name = this.name;
            var askany = ‘小王:’+name+’,我可以和小米在一起么?’;
            this._say(askany);
        },
        answer:function(){
            var name = this.name;
            var answerAny = name+”:我得想一下。。。”;
            this._say(answerAny);
            _this = this;
            setTimeout(function(){
                if(_this._isAgree()){
                    _this._say(name+’:好吧我同意了~’);
                    _this.promis.resolve();
                }else{
                    _this._say(name+’:我不同意’);

                    if(!_this._isfirst){
                        _this.promis.reject();
                        return;
                    }
                    _this._isfirst=false;
                    _this.begin();
                }
            },1000);
        },
        _isAgree:function(){
            var randomNum = parseInt(Math.random()*5);
            if(randomNum>=3){
                return false;
            }
            return true;
        },
        _say:function(any){
            $(“#logFrame”).append(‘<p>’+any+’</p>’);
        }
    }

    win.Request = Request;

})(window);

此时对单个人得请求就可以描述为以下方式:


Request("爸爸").begin();

可以发现在Request类中,有一个Promis类,这个Promis主要的作用是用来承接多个Request之间的状态流转。

promis类至少应该包含如下方法:

then:设置下一个任务

resolve:当前任务完成后,激活下一个任务

reject  :当前任务失败后,调用当前任务的fail方法

我对Promis的封装如下:

(function(win){
    function Promis(){

    }
    Promis.prototype = {
        task:null,
        nextTask:null,
        init:function(task){
            this.task = task;
        },
        then:function(task){
            this.nextTask = task;
            return task.promis;
        },
        resolve:function(){
            if(this.nextTask)
                this.nextTask.begin();
            else
                this.task.done();
        },
        reject:function(){
            this.task.fail();
        }
    }

    win.Promis = Promis;
    Promis.create=function(task){
        var p = new Promis();
        p.init(task);
        return p;
    }

    win.Deferred = function(task){
        task.begin();
        return task.promis;
    }

})(window);

如上,代码中有一个Deferred方法,用来激活首个任务。将Request和Promis两者组合在一起后,就可以使用链式写法完成以上需求,代码如下:

$(document).ready(function () {

    $(“#beginBtn”).on(“click”,function(){
        $(“#logFrame”).html(“”)
                      .append(‘<p>程序员小王想娶女神小米。</p>’);
        Deferred(Request(“爸爸”))
            .then(Request(“妈妈”))
            .then(Request(“大伯”))
            .then(Request(“小米”));
    });
});

最后附上html代码:

<!DOCTYPE html>
<html lang=“en”>
<head>
    <meta charset=“UTF-8”>
    <title>promis 实验</title>
    <script src=“http://gagalulu.wang/blog/js/jquery.js”>
    </script>
    <script src=“src/js/promis.js”></script>
    <script src=“src/js/request.js”></script>
    <script src=“src/js/index.js”></script>
    <style>
        *{
            margin: 0;
            padding: 0;
        }
        body{
            background:#333;
        }
        .main{
            width:100%;
            color:#fff;
        }
        .main .desc{
            width:90%;
            margin:25px auto;
            padding: 10px;
            border: 1px solid #3f3f3f;
        }
        .main .ctrl{
            width:90%;
            margin: 20px auto;

        }
        .main .ctrl .btn{
            color: #fff;
            border: 1px solid #3f3f3f;
            padding: 10px;
            border-radius:5px;
            cursor: pointer;
        }
        .main .log{
            width:90%;
            margin: 20px auto;
            min-height:300px;
            border: 1px solid #3f3f3f;
            padding: 10px;
        }
    </style>

</head>
<body>
    <div class=“main”>
        <div class=“desc”>
            <p>程序员小王想娶女神小米。</p>
            <p>小米说必须先让她爸爸认可,然后让妈妈认可,然后让大伯认可,且让妈妈认可的前提是爸爸认可,让大伯认可的前提是爸爸妈妈都认可,然后我在考虑~</p>
            <p>假设:他们各个人同意的可能性都为80%,而且都必须有一段时间考虑,每个人可以请求两次。</p>
            <p>问:小王能不能取到小米~?</p>
        </div>
        <div class=“ctrl”>
            <span class=“btn” id=“beginBtn”>开始求婚</span>
        </div>
        <div class=“log” id=“logFrame”>
            <p>程序员小王想娶女神小米。</p>
        </div>
    </div>
</body>
</html>

点击此处查看效果

对应的代码已经提交到github,大家可以fork观赏,有任何问题,欢迎在下面留言。

github地址:git@github.com:gagaprince/promis.git

转载请注明出处:http://gagalulu.wang/blog/detail/20 您的支持是我最大的动力!






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值