Promise原理浅析

Promise介绍

项目相关demo和代码地址

介绍

Promise 对象用于延迟(deferred) 计算和异步(asynchronous ) 计算.。一个Promise对象代表着一个还未完成,但预期将来会完成的操作。 Promise 对象是一个返回值的代理,这个返回值在promise对象创建时未必已知。它允许你为异步操作的成功或失败指定处理方法。 这使得异步方法可以像同步方法那样返回值:异步方法会返回一个包含了原返回值的 promise 对象来替代原返回值。

引自MDN

它解决什么问题

一个简单的示例 执行一个动画A,执行完之后再去执行另一个动画B

    setTimeout(function(){
        //A动画
        console.log('A');
        setTimeout(function() {
            //B动画
            console.log('B');
        },300)
    },300);

这里只有两个动画,如果有更多呢,就会看到一堆函数缩进

一种写法

浏览器实现方式 可以在支持Promise的版本上运行

var p = new Promise(function(resolve, reject){
  setTimeout(function(){
    //A动画
    console.log('A');
    resolve();
  },300);
});

p.then(function(){
  setTimeout(function() {
      //B动画
      console.log('B');
  },300);
});

另一种写法(jQuery版本)

jQuery版本的实现

var deferred = $.Deferred();
setTimeout(function(){
  //A动画
  console.log('A');
  deferred.resolve();
},300);

deferred.done(function() {
  setTimeout(function() {
    //B动画
    console.log('B');
  },300)
});

好像从代码上来看,是多了几行的样子,但是能用这种串行的方式来写,感觉一定很爽吧

Promise中的概念

Promise中有几个状态:

  • pending: 初始状态, 非 fulfilled 或 rejected.
  • fulfilled: 成功的操作.
  • rejected: 失败的操作.

这里从pending状态可以切换到fulfill状态(jQuery中是resolve状态),也可以从pengding切换到reject状态,这个状态切换不可逆,且fulfilled和reject两个状态之间是不能互相切换的。

一个简单版本的实现

/**
 * simple promise 
 * @param {[type]} fun [description]
 */
function PromiseB(fun) {

    this.succArg = undefined;
    this.failArg = undefined;
    this.succCbs = [];
    this.failCbs = [];
    this._status = this.STATUS.PENDING;

    this._execFun(fun);
}

PromiseB.prototype.STATUS = {
    PENDING: 1, //挂起状态
    RESOLVE: 2, //完成状态
    REJECT: 3 //拒绝状态
};

PromiseB.prototype._isFunction = function(f) {
    return Object.prototype.toString.call(f) === '[object Function]';
};

PromiseB.prototype._exec = function(callback, arg) {
    var newcallback;

    if (this._isFunction(callback)) {
        if (callback instanceof PromiseB) {
            callback.resolve(arg);
        } else {
            newcallback = new PromiseB(callback);
            newcallback.resolve(arg);
        }
    }
};

PromiseB.prototype._execFun = function(fun) {
    var that = this;

    if (this._isFunction(fun)) {
        fun(function() {
            that.succArg = Array.prototype.slice.apply(arguments);
            that._status = that.STATUS.RESOLVE;

            that.resolve.apply(that, arguments);
        }, function() {
            that.failArg = Array.prototype.slice.apply(arguments);
            that._status = that.STATUS.REJECT;

            that.reject.apply(that, arguments);
        });
    } else {
        this.resolve(fun);
    }

};

PromiseB.prototype.resolve = function() {
    var arg = arguments,
        ret,
        callback = this.succCbs.shift();
    if (this._status === this.STATUS.RESOLVE && callback) {
        ret = callback.apply(callback, arg);
        if (!(ret instanceof PromiseB)) {
            var _ret = ret;
            ret = new PromiseB(function(resolve) {
                setTimeout(function() {
                    resolve(_ret);
                });
            });

            ret.succCbs = this.succCbs.slice();
        }
        // this._exec(callback.apply(callback, arg), arg);
    }
};

PromiseB.prototype.reject = function() {
    var arg = arguments,
        ret,
        callback = this.failCbs.shift();
    if (this._status === this.STATUS.REJECT && callback) {
        ret = callback.apply(callback, arg);
        if (!(ret instanceof PromiseB)) {
            var _ret = ret;
            ret = new PromiseB(function(resolve) {
                setTimeout(function() {
                    resolve(_ret);
                }, 200);
            });
            ret.failCbs = this.failCbs.slice();
        }
    }
};

PromiseB.prototype.then = function(s, f) {
    this.done(s);
    this.fail(f);
    return this;
};

PromiseB.prototype.done = function(fun) {
    if (this._isFunction(fun)) {
        if (this._status === this.STATUS.RESOLVE) {
            fun.apply(fun, this.succArg);
        } else {
            this.succCbs.push(fun);
        }
    }
    return this;
};

PromiseB.prototype.fail = function(fun) {
    if (this._isFunction(fun)) {
        if (this._status === this.STATUS.REJECT) {
            fun.apply(fun, this.failArg);
        } else {
            this.failCbs.push(fun);
        }
    }
    return this;
};

PromiseB.prototype.always = function(fun) {
    this.done(fun);
    this.fail(fun);
    return this;
};

总结

  • promise会让代码变得更容易维护,像写同步代码一样写异步代码
  • 了解promise的原理,写个简单的实现版本就好了
  • promise的实现方案有很多,可以看这里

相关阅读

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏林德熙的博客

WPF 使用不安全代码快速从数组转 WriteableBitmap

先来说下以前的方法,以前使用的是 BitmapSource ,这个方法是大法官方提供的。

591
来自专栏前端学习心得

异步解决方案----Promise与Await

1424
来自专栏大内老A

WCF技术剖析之十九:深度剖析消息编码(Encoding)实现(下篇)

[爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济频道《天天山海经》为此录制的节目视频(苏州话)]]通过上篇的介绍,我们知道了WCF所有与编码与...

2019
来自专栏全沾开发(huā)

如何更好的编写async函数

1153
来自专栏Windows Community

Extensions in UWP Community Toolkit - Visual Extensions

概述 UWP Community Toolkit Extensions 中有一个为可视元素提供的扩展 - VisualExtensions,本篇我们结合代码详细...

2605
来自专栏流媒体人生

ATL Thunk机制学习

  ATL模板类库使用Thunk技术来实现与窗口消息相关联的HWND和负责处理消息的对象的this指针之间的映射。      ATL中窗口类注册时,窗口过程函数...

411
来自专栏专业duilib使用+业余界面开发

duilib 滚动条不能拖动 问题处理

1494
来自专栏十月梦想

异步函数async和await

前面我们介绍的是promise对象,这里我们介绍一下async...await异步函数,创建函数时候使用async关键词表示这是一个异步函数,await必须和a...

622
来自专栏知识分享

8-51单片机ESP8266学习-AT指令(测试TCP服务器--51单片机程序配置8266,做自己的手机TCP客户端发信息给单片机控制小灯的亮灭)

http://www.cnblogs.com/yangfengwu/p/8776712.html 先把源码和资料链接放到这里 链接:https://pan.ba...

4222
来自专栏令仔很忙

VB 子窗体被PictureBox控件挡住无法显示

   VB做机房收费系统的时候,用的MDI主窗体,在主窗体上加了一个Picturebox控件,运行的时候,点了子窗体,但是却没有出现,后来才发现,子窗体被Pic...

522

扫码关注云+社区