资讯专栏INFORMATION COLUMN

【入门】promise的实现

ruicbAndroid / 3437人阅读

摘要:状态机由于仅仅是一个状态机,我们必须从我们将会用到的状态信息开始考虑。观察方法我们现在有一个已经完成的状态机,但是我们仍然没有观察他的变化。

promise javascript 异步

这几天在看朴灵的深入浅出nodejs,看到异步编程的解决方案这一章时,卡在了promise这个地方。心中的念头就是自己动手写一个promise才好,于是就开始在网上找资料。

简介,这些代码首先是发表在Stack Overflow,是希望你能从中学习如何使用javascript实现一个promise,你将会更好的理解promise是如何的实现的。

状态机

由于promise仅仅是一个状态机,我们必须从我们将会用到的状态信息开始考虑。

var PENDDING = 0;
var FULFILLED = 1;
var REJECTED = 2;

function Promise(){
    //保存的状态可以为PENDDING,FULFILLED和REJECTED
    var state = PENDDING;
    //一旦FULFILLED或者REJECTED保存为value或者err
    var value = null;
    //保存成功或者失败的处理程序
    var handlers = []
}
转变

第二步,让我们考虑下面两种可能出现的情形,fulfilling和rejecting:

var PENDDING = 0;
var FULFILLED = 1;
var REJECTED = 2;

function Promise(){
    //保存的状态可以为PENDDING,FULFILLED和REJECTED
    var state = PENDDING;
    //一旦FULFILLED或者REJECTED保存为value或者err
    var value = null;
    //保存成功或者失败的处理程序
    var handlers = []

    function fullfill(result){
        state = FULFILLED;
        value = result;
    }

    function reject(error){
        state = REJECTED;
        value = error;
    }
}

上面这些给了我们较低等级的变换,但是我们还可以考虑额外更高级的叫做resolve的变换。

var PENDDING = 0;
var FULFILLED = 1;
var REJECTED = 2;

function Promise(){
    //保存的状态可以为PENDDING,FULFILLED和REJECTED
    var state = PENDDING;
    //一旦FULFILLED或者REJECTED保存为value或者err
    var value = null;
    //保存成功或者失败的处理程序
    var handlers = []

    function fulfill(result){
        state = FULFILLED;
        value = result;    
    }

    function reject(error){
        state = REJECTED;
        value = error;
    }

    function resolve(result){
        try{
            var then = getThen(result);
            if(then){
                doResolve(then.resolve(result),resolve,reject)
                return 
            }
            fullfill(result)
        }catch(e){
            reject(e)
        }
    }
}

注意无论传入resolve的是一个promise对象还是一个普通值,如果它是一个promise对象,等待他完成。一个promise对象不会进入fulfilled状态如果它还包含一个promise对象的话,因此我们将要暴露出的resolve函数强于内部的fulfill。我们用到了一系列的辅助函数,如下:

/**
 * 检查一个value是否是一个promise对象,如果是,返回那个promise的then方法
 *
 * @param {promise|any} value
 * @return {Function|null}
 */
function getThen(value){
    var t = typeof value;
    if(value && (t === "object"||t === "function")){
        var then = value.then;
        if(typeof then === "function"){
            return then
        }
    }
    return null
}
/**
 * 创建一个对潜在行为的处理方法并且保证onFulfilled和onRejected只被调用一次
 * 不对异步做保证
 *
 * @param {Function} fn A resolver function that may not be trusted
 * @param {Function} onFulfilled
 * @param {Function} onRejected
 */
function doResolve(fn,onFulfilled,onRejected){
    var done = false;
    try{
        fn(function(value){
            if(done) return
            done = true
            onFulfilled(value)
        },function(reason){
            if(done) return 
            done = true
            onRejected(reason)
        })
    }catch(ex){
        if(done) return 
        done = true
        onRejected(ex)
    }
}

构造

我们现在已经完成了内部的状态机,但同时我们还需要暴露一个处理或者观察该promise的方法,我们来增加一个处理promise的途径:

var PENDDING = 0;
var FULFILLED = 1;
var REJECTED = 2;

function Promise(fn){
    //保存的状态可以为PENDDING,FULFILLED和REJECTED
    var state = PENDDING;
    //一旦FULFILLED或者REJECTED保存为value或者err
    var value = null;
    //保存成功或者失败的处理程序
    var handlers = []


    function fulfill(result){
        state = FULFILLED;
        value = result;    
    }

    function reject(error){
        state = REJECTED;
        value = error;
    }

    function resolve(result){
        try{
            var then = getThen(result);
            if(then){
                doResolve(then.resolve(result),resolve,reject)
                return 
            }
            fullfill(result)
        }catch(e){
            reject(e)
        }
    }

    doResolve(fn,resolve,reject)
}

如你所见,我们复用了deResolve因为我们有另一个不被信任的处理器,fn函数可以调用resolvereject多次,甚至可以抛出异常,我们现在要做的是保证promise只执行或者拒绝一次,然后不在变换为其他的状态。

观察(.done方法)

我们现在有一个已经完成的状态机,但是我们仍然没有观察他的变化。我们最终要完成的是实现.then,但是由于.done简单很多于是我们就先实现它。
我们现在要实现promise.done(onFulfilled,onRejected)要满足以下的功能:
- onFulfilledonRejected中一个被调用
- 只被调用一次
- 在下一个tick(即.done方法返回后)时才被调用
- 它将会被调用不管promise是在我们调用.done之前或者之后被处理

var PENDDING = 0;
var FULFILLED = 1;
var REJECTED = 2;

function Promise(fn){
    //保存的状态可以为PENDDING,FULFILLED和REJECTED
    var state = PENDDING;
    //一旦FULFILLED或者REJECTED保存为value或者err
    var value = null;
    //保存成功或者失败的处理程序
    var handlers = []


    function fulfill(result){
        state = FULFILLED;
        value = result;
        handlers.forEach(handle);
        handlers = null
    }

    function reject(error){
        state = REJECTED;
        value = error;
        handlers.forEach(handle);
        handlers = null
    }

    function resolve(result){
        try{
            var then = getThen(result);
            if(then){
                doResolve(then.resolve(result),resolve,reject)
                return 
            }
            fullfill(result)
        }catch(e){
            reject(e)
        }
    }

    function handle(handler){
        if(state === PENDDING){
            handlers.push(handler)
        }else{
            if(state === FULFULLIED && typeof handler.onFulfilled === "function"){
                handler.onFulfilled(value);
            }
            if(state === REJECTED && typeof handler.onRejected === "function"){
                handler.onRejected(value);
            }
        }
    }

    this.done = function(onFulfilled,onRejected){
        //保证异步执行
        setTimeout(function(){
            handle({
                onFulfilled : onFulfilled,
                onRejected  : onRejected
            })
        },0)
    }

    doResolve(fn,resolve,reject)
}

我们得确定Promise被Resolved或者Rejected后处理程序可以得到通知,仅仅是在进入下一个tick时做这些。

观察(.then方法)

现在我们已经完成了.done方法,我们可以很类似的实现.then方法,只是要多构建一个Promise方法。

this.then = function(onFulfilled,onRejected){
    var self = this;
    return new Promise(function(resolve,Reject){
        return self.done(function(resolve){
                if(typeof onFulfilled === "function"){
                    try{
                        resolve(onFulfilled(result))
                    }catch(e){
                        reject(e)
                    }
                }else{
                    return resolve(result)
                }
        },function(error){
            if(typeof onRejected === "function"){
                try{
                    reject(onRejected(error))
                }catch(e){
                    reject(e)
                }
            }else{
                reject(error)
            }
        })
    })
}

以上翻译自Promise Implementing。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/85417.html

相关文章

  • js异步从入门到放弃(实践篇) — 常见写法&面试题解析

    摘要:前文该系列下的前几篇文章分别对不同的几种异步方案原理进行解析,本文将介绍一些实际场景和一些常见的面试题。流程调度里比较常见的一种错误是看似串行的写法,可以感受一下这个例子判断以下几种写法的输出结果辨别输出顺序这类题目一般出现在面试题里。 前文 该系列下的前几篇文章分别对不同的几种异步方案原理进行解析,本文将介绍一些实际场景和一些常见的面试题。(积累不太够,后面想到再补) 正文 流程调度...

    Awbeci 评论0 收藏0
  • 在非阻塞IO下nodejs下同步并行 ES6 promise入门深入(二)

    摘要:现在我们要用的重点就是我们的,这是一个能让函数并行的,可以基于多个。非常有用啊先上一个错误的代码这时候我们得到的就是数字了,而不是一个数组,这就是神奇所在。 看过 (一)的同学一定觉得这个Promise很简单,好像没什么可以用的地方,但是事实上,它的用处非常大啊,尤其是在nodejs上,愈加重要,虽然已经有大量的库实现了对Promise的封装了,不过我还是更倾向用原生的node来实现对...

    Jrain 评论0 收藏0
  • 在非阻塞IO下nodejs下同步并行 ES6 promise入门深入(二)

    摘要:现在我们要用的重点就是我们的,这是一个能让函数并行的,可以基于多个。非常有用啊先上一个错误的代码这时候我们得到的就是数字了,而不是一个数组,这就是神奇所在。 看过 (一)的同学一定觉得这个Promise很简单,好像没什么可以用的地方,但是事实上,它的用处非常大啊,尤其是在nodejs上,愈加重要,虽然已经有大量的库实现了对Promise的封装了,不过我还是更倾向用原生的node来实现对...

    verano 评论0 收藏0
  • Promise入门之基本用法

    摘要:入门之基本用法背景在我们使用异步函数比如进行编写代码,如果我们需要很多个请求不同的接口,而下一个接口需要依赖上一个接口的返回值,这样,我们的代码则需要在各种回调函数中嵌套,这样一层一层地下去,就形成了回调地狱。 Promise入门之基本用法 背景 在我们使用异步函数比如ajax进行编写代码,如果我们需要很多个ajax请求不同的接口,而下一个接口需要依赖上一个接口的返回值,这样,我们的代...

    siberiawolf 评论0 收藏0
  • Promise快速入门

    摘要:周五就想写这篇文章,但是无奈花花世界的诱惑太多就一直拖到了今天,自责遍进入正题对象用于表示一个异步操作的最终状态完成或失败,以及其返回的值。 周五就想写这篇文章,但是无奈花花世界的诱惑太多……就一直拖到了今天,自责1e4遍;进入正题Promise: Promise 对象用于表示一个异步操作的最终状态(完成或失败),以及其返回的值。 上为MDNPromise的定义;ES6规定Promis...

    bergwhite 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<