首页 文章

基本的Javascript承诺实现尝试

提问于
浏览
48

为了更好地理解promises如何在Javascript中工作,我决定尝试自己编写代码基本实现 .

基本上我想实现Promises Object(我在代码中称之为Aaa),它将函数作为参数 . 此函数可以调用解析 resolve 的承诺,或拒绝 reject 它 . 基本实现和用法如下 . 根据承诺规范,不确定第二个参数是否可接受,但这是我到目前为止所得到的 .

Aaa=function(f,pause) { 

    console.log("ggg");

    var t=this;
    this.f=f;
    this.thens=[];

    this.resolve=function(g) {

        for(var i=0;i<t.thens.length;i++)
        {
            // try/catch to be used later for dealing with exceptions

            try
            {
                t.thens[i].f(g);
                t.thens[i].resolve();
            }   
            catch(ex)
            {}

        }
    };  

    // to be implemented later
    this.reject=function(g) {};

    this.then=function(resolve,reject) {

        // i'm passing true for pause argument as we dont need to execute promise code just yet
        var nextPromise=new Aaa(resolve,true);

        this.thens.push(nextPromise);

        return nextPromise;
    }


    if(!pause)
        this.f(this.resolve,this.reject); 

}


var aaa=new Aaa(function(resolve,reject) {

    console.log("aaa");

    setTimeout(function() {

        console.log("fff");
        resolve("good");

    },2000);

    console.log("bbb");

});

所以现在可以创建,调用和解决承诺 . 每个 then 方法都会返回新的Aaa(Promise),因此可以将它们链接起来 . 现在,下面的代码使用上面创建的promise和链 then 回调 . 每个 then 返回新的承诺,在这种情况下它似乎工作正常:

aaa.then(function(res) {

    console.log("ccc");
    console.log(res);

})
.then(function(res) {
    console.log("ddd");
    console.log(res);
},function(rej) {
    console.log("eee");
    console.log(rej);
});

我得到的输出是:

ggg
aaa 
bbb 
ggg 
ggg 
fff 
ccc 
good 
ddd 
undefined

但是,当 then 之一的调用返回一个promise时,问题是:

aaa.then(function(res) {

    console.log("ccc");
    console.log(res);

    // here we return the promise manually. then next then call where "ddd" is output should not be called UNTIL this promise is resolved. How to do that?

        return new Aaa(function(resolve,reject) {

        console.log("iii");

        setTimeout(function() {
        console.log("kkk");
            resolve("good2");
            // reject("bad");

        },2000);

        console.log("jjj");

    }).then(function (res) {
        console.log("lll");

        console.log(res);
    });

})
.then(function(res) {
    console.log("ddd");
    console.log(res);
},function(rej) {
    console.log("eee");
    console.log(rej);
});

输出是:

ggg 
aaa 
bbb 
ggg 
ggg  
fff  
ccc  
good  
ggg  
iii  
jjj  
ggg  
ddd  
undefined  
kkk  
lll  
good2

然后不应该调用输出 ddd 的调用UNTIL我们刚刚添加的返回的promise将被解析 .

如何才能最好地实施?

4 回答

  • 12

    这一切看起来都非常复杂 . 我认为这是一个非常简单的递归解决方案 . 为了简洁起见,我将省略拒绝,但除了你停止链之外,它几乎与解决方案相同 .

    var MyPromise = function(callback) {
      this.callbacks = [];
      callback(this.resolve.bind(this));
     }
    
    MyPromise.prototype.resolve = function(data) {
      var callback = this.callbacks.pop();
      var result =  callback(data);
    
      if (!result) return;
    
      if (result instanceof MyPromise) {
        var resolve = this.resolve.bind(this);
        return result.then(function(d) {
            return resolve(d);
        });
      }
    
      return this.resolve(result);
    
    }
    
    MyPromise.prototype.then = function(callback) {
      this.callbacks.unshift(callback);
      return this;
    }
    
  • 78

    你在这里处理的案例很多 . 最好的办法是将承诺 Build 为状态机:

    var PENDING = 0;
    var FULFILLED = 1;
    var REJECTED = 2;
    
    function Promise() {
    
      // store state which can be PENDING, FULFILLED or REJECTED
      var state = PENDING;
    
      // store value once FULFILLED or REJECTED
      var value = null;
    
      // store sucess & failure handlers
      var handlers = [];
    }
    

    现在让我们定义一个简单的帮助器,通过我们的其余实现来使用:

    // a function that returns `then` if `value` is a promise, otherwise `null`
    function getThen(value) {
      if (result && (typeof result === 'object' || typeof result === 'function')) {
        var then = value.then;
        if (typeof then === 'function') {
          return then;
        }
      }
      return null;
    }
    

    接下来,我们需要考虑可能发生的每个转换:

    var PENDING = 0;
    var FULFILLED = 1;
    var REJECTED = 2;
    
    function Promise() {
    
      // store state which can be PENDING, FULFILLED or REJECTED
      var state = PENDING;
    
      // store value once FULFILLED or REJECTED
      var value = null;
    
      // store sucess & failure handlers
      var handlers = [];
    
      function resolve(result) {
        try {
          var then = getThen(result);
          if (then) {
            doResolve(then.bind(result), resolve, reject)
            return
          }
          state = FULFILLED;
          value = result;
        } catch (e) {
          reject(e);
        }
      }
    
      function reject(error) {
        state = REJECTED;
        value = error;
      }
    }
    

    注意 resolve 如何接收Promise作为其参数,但是Promise永远不能用另一个Promise来实现 . 所以我们必须处理这个特例 .

    另请注意,Promise只能被实现/拒绝一次 . 我们还有第三方承诺可能行为不端的问题,我们应该保护我们的代码 . 出于这个原因,我还没有在 resolve 内调用 result.then(resolve, reject) . 相反,我把它分成一个单独的函数:

    /**
     * Take a potentially misbehaving resolver function and make sure
     * onFulfilled and onRejected are only called once.
     *
     * Makes no guarantees about asynchrony.
     */
    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)
      }
    }
    

    所以现在我们有一个完整的状态机,但无法观察或触发状态的变化 . 让我们首先添加一种通过传入解析器函数来触发状态更改的方法 .

    function Promise(fn) {
      if (typeof this !== 'object')
        throw new TypeError('Promises must be constructed via new');
      if (typeof fn !== 'function')
        throw new TypeError('fn must be a function');
    
      // store state which can be PENDING, FULFILLED or REJECTED
      var state = PENDING;
    
      // store value once FULFILLED or REJECTED
      var value = null;
    
      // store sucess & failure handlers
      var handlers = [];
    
      function resolve(result) {
        try {
          var then = getThen(result);
          if (then) {
            doResolve(then.bind(result), resolve, reject)
            return
          }
          state = FULFILLED;
          value = result;
        } catch (e) {
          reject(e);
        }
      }
    
      function reject(error) {
        state = REJECTED;
        value = error;
      }
    
      doResolve(fn, resolve, reject);
    }
    

    如您所见,我们重新使用 doResolve 因为我们有另一个不受信任的解析器 . fn 可能会多次调用 resolvereject ,并且可能会抛出错误 . 我们需要处理所有这些情况(这就是 doResolve 所做的) .

    我们现在已经完成了状态机,但是我们没有公开任何关于它所处状态的信息 . 让我们尝试添加 .done(onFulfilled, onRejected) 方法,就像 .then 一样,除了它不返回Promise并且不处理 onFulfilled 抛出的错误和 onRejected .

    var PENDING = 0;
    var FULFILLED = 1;
    var REJECTED = 2;
    
    function Promise(fn) {
      if (typeof this !== 'object')
        throw new TypeError('Promises must be constructed via new');
      if (typeof fn !== 'function')
        throw new TypeError('fn must be a function');
    
      // store state which can be PENDING, FULFILLED or REJECTED
      var state = PENDING;
    
      // store value once FULFILLED or REJECTED
      var value = null;
    
      // store sucess & failure handlers
      var handlers = [];
    
      function resolve(result) {
        try {
          var then = getThen(result);
          if (then) {
            doResolve(then.bind(result), resolve, reject)
            return
          }
          state = FULFILLED;
          value = result;
          handlers.forEach(handle);
          handlers = null;
        } catch (e) {
          reject(e);
        }
      }
    
      function reject(error) {
        state = REJECTED;
        value = error;
        handlers.forEach(handle);
        handlers = null;
      }
    
      function handle(handler) {
        if (state === PENDING) {
          handlers.push(handler);
        } else {
          if (state === FULFILLED && typeof handler.onFulfilled === 'function') {
            handler.onFulfilled(value);
          }
          if (state === REJECTED && typeof handler.onRejected === 'function') {
            handler.onRejected(value);
          }
        }
      }
      this.done = function (onFulfilled, onRejected) {
        setTimeout(function () { // ensure we are always asynchronous
          handle({
            onFulfilled: onFulfilled,
            onRejected: onRejected
          });
        }, 0);
      }
    
      doResolve(fn, resolve, reject);
    }
    

    注意我们必须处理在Promise变为满足/拒绝之前和之后调用 .done 的情况 .

    我们几乎有一个完整的promise实现,但是,正如您在构建自己的实现时已经注意到的那样,我们需要一个返回Promise的 .then 方法 .

    我们可以在 .done 中轻松构建这个:

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

    请注意我们如何获得您现在免费获得的东西,因为 resolve 接受了Promise并等待它被解决 .

    N.B. 我没有测试过这个Promise的实现(虽然据我所知是正确的) . 您应该测试针对Promises / A测试套件构建的任何实现(https://github.com/promises-aplus/promises-tests),并且还可以找到Promises / A规范(https://github.com/promises-aplus/promises-spec),用于确定算法的任何特定部分的正确行为 . 作为最终资源,promise是Promise规范的一个非常小的实现 .

  • 0

    (有关完整的Promise实现,请向下滚动) .

    代码中的一些问题

    有几个问题,但我认为你的代码中的主要错误是你将 then 方法的参数作为参数传递给一个新的promise:

    this.then=function(resolve,reject) {
        var nextPromise=new Aaa(resolve,true);
        // ...
    

    虽然两个参数都是回调函数,但它们具有不同的签名,并且用于完全不同的目的:

    • promise构造函数的参数是一个回调函数,它将立即同步执行 . 函数作为第一个参数传递给它,您可以使用它来解析您正在创建的承诺 .

    • then 方法的(第一个)参数是一个回调函数,只有在解析基本承诺时才会异步执行,并将解析后的值作为参数传递给它 .

    您可以在代码中看到差异,您可以将构造函数的参数存储为f属性 . 你有这两个:

    t.thens[i].f(g);
    

    ...其中g是已解析的值,但也是:

    this.f(this.resolve,this.reject);
    

    ......参数是函数 . 当你创建nextPromise时,你实际上首先用这两个参数调用f,然后用g参数调用 .

    从头开始实现Promises / A兼容的实施

    我们可以按照Promises/A+ specification中的要求构建我们自己的Promise实现:

    2.1承诺状态

    只允许2个状态转换:从挂起到履行,从挂起到拒绝 . 不可能进行其他转换,并且一旦执行转换,承诺值(或拒绝原因)不应更改 .

    这是一个符合上述限制的简单实现 . 注释参考上述规范中的编号要求:

    function MyPromise(executor) {
        this.state = 'pending';
        this.value = undefined;
        executor(this.resolve.bind(this), this.reject.bind(this));
    }
    
    // 2.1.1.1: provide only two ways to transition
    MyPromise.prototype.resolve = function (value) {
        if (this.state !== 'pending') return; // 2.1.2.1, 2.1.3.1: cannot transition anymore
        this.state = 'fulfilled'; // 2.1.1.1: can transition
        this.value = value; // 2.1.2.2: must have a value
    }
    
    MyPromise.prototype.reject = function (reason) {
        if (this.state !== 'pending') return; // 2.1.2.1, 2.1.3.1: cannot transition anymore
        this.state = 'rejected'; // 2.1.1.1: can transition
        this.value = reason; // 2.1.3.2: must have a reason
    }
    

    当然,这不提供 then 方法,这是Promises的关键:

    2.2当时的方法

    这是规范的核心 . 上面的代码可以扩展为公开 then 方法,它返回一个promise并提供相应的 then 回调的异步执行,只提供一次,提供多个 then 调用,将异常转为拒绝,等等 .

    所以下面的代码添加了 then 方法,但也添加了一个单独定义的 broadcast 函数,因为它必须在任何状态更改时调用:这不仅包括 then 方法的效果(将promise添加到列表中),而且 resolvereject 方法(状态和值的变化) .

    function MyPromise(executor) {
        this.state = 'pending';
        this.value = undefined;
        // A list of "clients" that need to be notified when a state
        //   change event occurs. These event-consumers are the promises
        //   that are returned by the calls to the `then` method.
        this.consumers = [];
        executor(this.resolve.bind(this), this.reject.bind(this));
    }
    
    // 2.1.1.1: provide only two ways to transition
    MyPromise.prototype.resolve = function (value) {
        if (this.state !== 'pending') return; // 2.1.2.1, 2.1.3.1: cannot transition anymore
        this.state = 'fulfilled'; // 2.1.1.1: can transition
        this.value = value; // 2.1.2.2: must have a value
        this.broadcast();
    }    
    
    MyPromise.prototype.reject = function (reason) {
        if (this.state !== 'pending') return; // 2.1.2.1, 2.1.3.1: cannot transition anymore
        this.state = 'rejected'; // 2.1.1.1: can transition
        this.value = reason; // 2.1.3.2: must have a reason
        this.broadcast();
    }    
    
    // A promise’s then method accepts two arguments:
    MyPromise.prototype.then = function(onFulfilled, onRejected) {
        var consumer = new MyPromise(function () {});
        // 2.2.1.1 ignore onFulfilled if not a function
        consumer.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null;
        // 2.2.1.2 ignore onRejected if not a function
        consumer.onRejected = typeof onRejected === 'function' ? onRejected : null;
        // 2.2.6.1, 2.2.6.2: .then() may be called multiple times on the same promise
        this.consumers.push(consumer);
        // It might be that the promise was already resolved... 
        this.broadcast();
        // 2.2.7: .then() must return a promise
        return consumer;
    };
    
    MyPromise.prototype.broadcast = function() {
        var promise = this;
        // 2.2.2.1, 2.2.2.2, 2.2.3.1, 2.2.3.2 called after promise is resolved
        if (this.state === 'pending') return;
        // 2.2.6.1, 2.2.6.2 all respective callbacks must execute
        var callbackName = this.state == 'fulfilled' ? 'onFulfilled' : 'onRejected';
        var resolver = this.state == 'fulfilled' ? 'resolve' : 'reject';
        // 2.2.4 onFulfilled/onRejected must be called asynchronously
        setTimeout(function() {
            // 2.2.6.1, 2.2.6.2 traverse in order, 2.2.2.3, 2.2.3.3 called only once
            promise.consumers.splice(0).forEach(function(consumer) {
                try {
                    var callback = consumer[callbackName];
                    // 2.2.1.1, 2.2.1.2 ignore callback if not a function, else
                    // 2.2.5 call callback as plain function without context
                    if (callback) {
                        // TODO: 2.2.7.1. For now we simply fulfill the promise:
                        consumer.resolve(callback(promise.value)); 
                    } else {
                        // 2.2.7.3 resolve in same way as current promise
                        consumer[resolver](promise.value);
                    }
                } catch (e) {
                    // 2.2.7.2
                    consumer.reject(e);
                };
            })
        });
    };
    

    这几乎涵盖了所有内容,除了在 TODO: 评论中,必须调用所谓的Promise Resolution Procedure:

    2.3承诺解决程序

    这是一个以不同方式处理值(甚至是promises)的过程:不是按原样返回值,程序将对该值执行 then 方法,并使用从 then 回调接收的值异步履行保证 . 在规范中没有提到它,但这不仅在 then 方法中执行,而且在使用这样的值解决主要承诺时也很有趣 .

    因此现有的 resolve 方法应该替换为"Promise Resolution Procedure",它将调用原始方法 . 最初的一个可以被称为"fulfill",表示它将解决承诺总是满足:

    function MyPromise(executor) {
        this.state = 'pending';
        this.value = undefined;
        // A list of "clients" that need to be notified when a state
        //   change event occurs. These event-consumers are the promises
        //   that are returned by the calls to the `then` method.
        this.consumers = [];
        executor(this.resolve.bind(this), this.reject.bind(this));
    }
    
    // 2.1.1.1: provide only two ways to transition
    MyPromise.prototype.fulfill = function (value) {
        if (this.state !== 'pending') return; // 2.1.2.1, 2.1.3.1: cannot transition anymore
        this.state = 'fulfilled'; // 2.1.1.1: can transition
        this.value = value; // 2.1.2.2: must have a value
        this.broadcast();
    }    
    
    MyPromise.prototype.reject = function (reason) {
        if (this.state !== 'pending') return; // 2.1.2.1, 2.1.3.1: cannot transition anymore
        this.state = 'rejected'; // 2.1.1.1: can transition
        this.value = reason; // 2.1.3.2: must have a reason
        this.broadcast();
    }    
    
    // A promise’s then method accepts two arguments:
    MyPromise.prototype.then = function(onFulfilled, onRejected) {
        var consumer = new MyPromise(function () {});
        // 2.2.1.1 ignore onFulfilled if not a function
        consumer.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null;
        // 2.2.1.2 ignore onRejected if not a function
        consumer.onRejected = typeof onRejected === 'function' ? onRejected : null;
        // 2.2.6.1, 2.2.6.2: .then() may be called multiple times on the same promise
        this.consumers.push(consumer);
        // It might be that the promise was already resolved... 
        this.broadcast();
        // 2.2.7: .then() must return a promise
        return consumer;
    };
    
    MyPromise.prototype.broadcast = function() {
        var promise = this;
        // 2.2.2.1, 2.2.2.2, 2.2.3.1, 2.2.3.2 called after promise is resolved
        if (this.state === 'pending') return;
        // 2.2.6.1, 2.2.6.2 all respective callbacks must execute
        var callbackName = this.state == 'fulfilled' ? 'onFulfilled' : 'onRejected';
        var resolver = this.state == 'fulfilled' ? 'resolve' : 'reject';
        // 2.2.4 onFulfilled/onRejected must be called asynchronously
        setTimeout(function() {
            // 2.2.6.1, 2.2.6.2 traverse in order, 2.2.2.3, 2.2.3.3 called only once
            promise.consumers.splice(0).forEach(function(consumer) {
                try {
                    var callback = consumer[callbackName];
                    // 2.2.1.1, 2.2.1.2 ignore callback if not a function, else
                    // 2.2.5 call callback as plain function without context
                    if (callback) {
                        // 2.2.7.1. execute the Promise Resolution Procedure:
                        consumer.resolve(callback(promise.value)); 
                    } else {
                        // 2.2.7.3 resolve in same way as current promise
                        consumer[resolver](promise.value);
                    }
                } catch (e) {
                    // 2.2.7.2
                    consumer.reject(e);
                };
            })
        });
    };
    
    // The Promise Resolution Procedure: will treat values that are thenables/promises
    // and will eventually call either fulfill or reject/throw.
    MyPromise.prototype.resolve = function(x) {
        var wasCalled, then;
        // 2.3.1
        if (this === x) {
            throw new TypeError('Circular reference: promise value is promise itself');
        }
        // 2.3.2
        if (x instanceof MyPromise) {
            // 2.3.2.1, 2.3.2.2, 2.3.2.3
            x.then(this.resolve.bind(this), this.reject.bind(this));
        } else if (x === Object(x)) { // 2.3.3
            try {
                // 2.3.3.1
                then = x.then;
                if (typeof then === 'function') {
                    // 2.3.3.3
                    then.call(x, function resolve(y) {
                        // 2.3.3.3.3 don't allow multiple calls
                        if (wasCalled) return;
                        wasCalled = true;
                        // 2.3.3.3.1 recurse
                        this.resolve(y);
                    }.bind(this), function reject(reasonY) {
                        // 2.3.3.3.3 don't allow multiple calls
                        if (wasCalled) return;
                        wasCalled = true;
                        // 2.3.3.3.2
                        this.reject(reasonY);
                    }.bind(this));
                } else {
                    // 2.3.3.4
                    this.fulfill(x);
                }
            } catch(e) {
                // 2.3.3.3.4.1 ignore if call was made
                if (wasCalled) return;
                // 2.3.3.2 or 2.3.3.3.4.2
                this.reject(e);
            }
        } else {
            // 2.3.4
            this.fulfill(x);
        }
    }
    

    现在这是Promises / A兼容的,至少它通过了测试套件 . 然而,Promise对象暴露了太多的方法和属性:

    只有一个Promise对象

    上面构建的构造函数创建的东西更像是Deferred对象,即暴露 resolvereject 方法 . 更糟糕的是, statusvalue 属性是可写的 . 因此,将此视为不安全的Deferred对象的构造函数更合乎逻辑,并创建一个单独的Promise构造函数,该构造函数构建于此,但仅公开所需内容: then 方法和可以访问 resolvereject 的构造函数回调 .

    然后,延迟对象可以不使用构造函数回调参数,并通过 promise 属性提供对纯promise对象的访问:

    function Deferred() {
        this.state = 'pending';
        this.value = undefined;
        this.consumers = [];
        this.promise = Object.create(MyPromise.prototype, {
            then: { value: this.then.bind(this) }
        });
    }
    
    // 2.1.1.1: provide only two ways to transition
    Deferred.prototype.fulfill = function (value) {
        if (this.state !== 'pending') return; // 2.1.2.1, 2.1.3.1: cannot transition anymore
        this.state = 'fulfilled'; // 2.1.1.1: can transition
        this.value = value; // 2.1.2.2: must have a value
        this.broadcast();
    }    
    
    Deferred.prototype.reject = function (reason) {
        if (this.state !== 'pending') return; // 2.1.2.1, 2.1.3.1: cannot transition anymore
        this.state = 'rejected'; // 2.1.1.1: can transition
        this.value = reason; // 2.1.3.2: must have a reason
        this.broadcast();
    }    
    
    // A promise’s then method accepts two arguments:
    Deferred.prototype.then = function(onFulfilled, onRejected) {
        var consumer = new Deferred();
        // 2.2.1.1 ignore onFulfilled if not a function
        consumer.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null;
        // 2.2.1.2 ignore onRejected if not a function
        consumer.onRejected = typeof onRejected === 'function' ? onRejected : null;
        // 2.2.6.1, 2.2.6.2: .then() may be called multiple times on the same promise
        this.consumers.push(consumer);
        // It might be that the promise was already resolved... 
        this.broadcast();
        // 2.2.7: .then() must return a promise
        return consumer;
    };
    
    Deferred.prototype.broadcast = function() {
        var promise = this;
        // 2.2.2.1, 2.2.2.2, 2.2.3.1, 2.2.3.2 called after promise is resolved
        if (this.state === 'pending') return;
        // 2.2.6.1, 2.2.6.2 all respective callbacks must execute
        var callbackName = this.state == 'fulfilled' ? 'onFulfilled' : 'onRejected';
        var resolver = this.state == 'fulfilled' ? 'resolve' : 'reject';
        // 2.2.4 onFulfilled/onRejected must be called asynchronously
        setTimeout(function() {
            // 2.2.6.1, 2.2.6.2 traverse in order, 2.2.2.3, 2.2.3.3 called only once
            promise.consumers.splice(0).forEach(function(consumer) {
                try {
                    var callback = consumer[callbackName];
                    // 2.2.1.1, 2.2.1.2 ignore callback if not a function, else
                    // 2.2.5 call callback as plain function without context
                    if (callback) {
                        // 2.2.7.1. execute the Promise Resolution Procedure:
                        consumer.resolve(callback(promise.value)); 
                    } else {
                        // 2.2.7.3 resolve in same way as current promise
                        consumer[resolver](promise.value);
                    }
                } catch (e) {
                    // 2.2.7.2
                    consumer.reject(e);
                };
            })
        });
    };
    
    // The Promise Resolution Procedure: will treat values that are thenables/promises
    // and will eventually call either fulfill or reject/throw.
    Deferred.prototype.resolve = function(x) {
        var wasCalled, then;
        // 2.3.1
        if (this.promise === x) {
            throw new TypeError('Circular reference: promise value is promise itself');
        }
        // 2.3.2
        if (x instanceof MyPromise) {
            // 2.3.2.1, 2.3.2.2, 2.3.2.3
            x.then(this.resolve.bind(this), this.reject.bind(this));
        } else if (x === Object(x)) { // 2.3.3
            try {
                // 2.3.3.1
                then = x.then;
                if (typeof then === 'function') {
                    // 2.3.3.3
                    then.call(x, function resolve(y) {
                        // 2.3.3.3.3 don't allow multiple calls
                        if (wasCalled) return;
                        wasCalled = true;
                        // 2.3.3.3.1 recurse
                        this.resolve(y);
                    }.bind(this), function reject(reasonY) {
                        // 2.3.3.3.3 don't allow multiple calls
                        if (wasCalled) return;
                        wasCalled = true;
                        // 2.3.3.3.2
                        this.reject(reasonY);
                    }.bind(this));
                } else {
                    // 2.3.3.4
                    this.fulfill(x);
                }
            } catch(e) {
                // 2.3.3.3.4.1 ignore if call was made
                if (wasCalled) return;
                // 2.3.3.2 or 2.3.3.3.4.2
                this.reject(e);
            }
        } else {
            // 2.3.4
            this.fulfill(x);
        }
    }
    
    function MyPromise(executor) {
        // A Promise is just a wrapper around a Deferred, exposing only the `then`
        // method, while `resolve` and `reject` are available in the constructor callback
        var df = new Deferred();
        // Provide access to the `resolve` and `reject` methods via the callback
        executor(df.resolve.bind(df), df.reject.bind(df));
        return df.promise;
    }
    

    这个代码有几种可能的优化,例如使Deferred方法成为私有函数,并将类似的代码合并到更短的代码块中,但是现在它很清楚地显示了每个需求的覆盖范围 .

    快乐的编码 .

  • 0

    my solution

    function Promise(resolver){
        if(typeof resolver !== 'function') {
            throw new TypeError(`Promise resolver ${resolver} is not a function`)
        }
        this.state = 'pending'
        this.value = void 0
        try{
            resolver(this.resolve.bind(this), this.reject.bind(this))
        }catch(error){
            this.reject.call(this,error)
        }
    }
    
    Promise.prototype.resolve = function(value) {
        if(this.state !== 'pending') return
        this.value = value
        this.state = 'fulfilled'    
        setTimeout( () => {
            if(!this.onFulfilled) return
            this.onFulfilled(value)
        }, 0)
    };
    
    Promise.prototype.reject = function(reason){
        if(this.state !== 'pending') return
        this.value = reason
        this.state = 'rejected'
        setTimeout( () => {
            if(this.onRejected){
                this.onRejected(reason)
            }else{
                throw `Uncaught (in promise) ${reason}`
            }
        }, 0)
    };
    
    Promise.prototype.then = function(fulfilled, rejected){
        if ( typeof fulfilled !== 'function' && typeof rejected !== 'function' ) {
            return this;
        }
        if (typeof fulfilled !== 'function' && this.state === 'fulfilled' ||
            typeof rejected !== 'function' && this.state === 'rejected') {
            return this;
        }
        var self = this
        return new Promise( (resolve, reject) => {
            if(fulfilled && typeof fulfilled == "function"){
                var onFulfilled = function (){
                    try{
                        var result = fulfilled(self.value)
                        if(result && typeof result.then === 'function'){
                            result.then(resolve, reject)
                        }else{
                            resolve(result)
                        }
                    }catch(error){
                        reject(error)
                    }
                }
                if(self.state === 'pending'){
                    self.onFulfilled = onFulfilled
                }else if(self.state === 'fulfilled'){
                    onFulfilled()
                }
            }
            if(rejected && typeof rejected == "function"){
                var onRejected = function (){
                    try{
                        var result = rejected(self.value)
                        if(result && typeof result.then === 'function'){
                            result.then(resolve, reject)
                        }else{
                            resolve(result)
                        }
                    }catch(error){
                        reject(error)
                    }
                }
                if( self.state === 'pending'){
                    self.onRejected = onRejected
                }else if(self.state === 'rejected'){
                    onRejected()
                }
            }
        })
    }
    
    /*
     *  the methods don't in Promise/A+ 
     */
    Promise.prototype.catch = function(onRejected){
        return this.then(null, onRejected)
    }
    
    Promise.all = function(iterable){
        if(typeof iterable[Symbol.iterator] !== 'function'){
            throw new TypeError(`${iterable[Symbol.iterator]} is not a function`)
        }
        // Array,TypedArray,String,arguments ==> length; Map,Set ==> size 
        let len = [...iterable].length, i = 0, counter = 0, res = [];
        return new Promise( (resolve, reject) => {
            for(let item of iterable){
                ( (i) => {
                    Promise.resolve(item).then(function(value){
                        counter++
                        res[i] = value
                        if(counter == len){
                            resolve(res)
                        }
                    },function(reason){
                        if(!called){
                            reject(reason)
                        }
                    })
                })(i++)
            }
        })
    }
    
    Promise.race = function(iterable){
        if(typeof iterable[Symbol.iterator] !== 'function'){
            throw new TypeError(`${iterable[Symbol.iterator]} is not a function`)
        }
        return new Promise( (resolve,reject) => {
            for(let item of iterable){
                Promise.resolve(item).then(function(value){
                    resolve(value)
                },function(reason){
                    reject(reason)
                })
            }
        })
    }
    
    Promise.resolve = function(value){
        //if(value instanceof this) return value
        //if(value instanceof Promise) return value
        if(value.constructor !== Promise) return value
        return new Promise( (resolve,reject) => {
            if(value && typeof value === 'object' && typeof value.then === 'function'){
                resolve( value.then( v => v))
            }else{
                resolve(value)
            }
        })
    }
    
    Promise.reject = function(reason){
        return new Promise( (resolve,reject) => {
            reject(reason)
        })
    }
    

相关问题