基本的 Javascript promise 实现尝试 [英] Basic Javascript promise implementation attempt

查看:19
本文介绍了基本的 Javascript promise 实现尝试的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为了更好地了解 Javascript 中的 Promise 是如何工作的,我决定尝试一下并自己编写基本实现.

To gain better understanding of how promises work in Javascript I decided to give it a try and code basic implementation myself.

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

Basically I want to implement Promises Object (I call it Aaa in my code) that takes function as an argument. This function can call resolve to resolve the promise, or reject to reject it. The basic implementation and usage is below. Not sure if the second argument is accepteable according to promise specs, but that's what I got so far.

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");

});

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

So now the promise can be created, called and resolved. Each then method will return new Aaa (Promise) so these can be chained. Now the code below uses promise created above and chains then callbacks. Each then returns new promise and in this case it seems to work fine:

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 调用之一返回承诺时:

The problem is however when one of the then calls returns a 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 输出的调用不应该被调用,直到我们刚刚添加的返回的承诺被解决.

The call then where ddd is output should not be called UNTIL the returned promise we just added is resolved.

如何最好地实施?

推荐答案

这里有很多情况您没有处理.最好的办法是首先将 promise 构建为状态机:

There are a number of cases you're not handling here. The best bet is to start by building the promise as a state machine:

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 = [];
}

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

Now lets define a simple helper to use through the rest of our implementation:

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

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

Next, we need to consider each of the transformations that can occur:

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 来实现.所以我们必须处理这种特殊情况.

Note how resolve can receive a Promise as its argument, but a Promise can never be fulfilled with another Promise. So we have to handle this special case.

还要注意,一个 Promise 只能被完成/拒绝一次.我们也有第三方 Promise 可能行为不端的问题,我们应该保护我们的代码免受这种情况的影响.出于这个原因,我不仅在 resolve 中调用了 result.then(resolve, reject).相反,我将其拆分为一个单独的函数:

Note also that a Promise can only ever be fulfilled/rejected once. We also have the problem that a third party Promise may misbehave, and we should guard our code against that. For this reason, I haven't just called result.then(resolve, reject) from within resolve. Instead, I split that into a separate function:

/**
 * 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)
  }
}

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

So now we have a completed state machine, but no way to observe or trigger the changes in state. Lets start by adding a way to trigger the state changes by passing in a resolver function.

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 所做的).

As you can see, we re-use doResolve because we have another un-trusted resolver. The fn might call resolve or reject multiple times, and it might throw an error. We need to handle all of these cases (and that's what doResolve does).

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

We now have the completed state machine, but we haven't exposed any information about what state it is in. Lets try adding a .done(onFulfilled, onRejected) method that is just like .then except that it does not return a Promise and does not handle errors thrown by onFulfilled and 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);
}

注意我们必须如何处理 .done 在 Promise 实现/拒绝之前和之后都被调用的情况.

Note how we must handle the case of .done being called both before and after the Promise becomes fulfilled/rejected.

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

We almost have a complete promise implementation, but, as you already noticed when building your own implementation, we need a .then method that returns a Promise.

我们可以用 .done 轻松构建:

We can build this easilly out of .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 并等待它被解决.

Note here how we get the thing you were struggling with for free now, because resolve accepts a Promise and waits for it to be resolved.

注意我还没有测试过这个 Promise 实现(尽管据我所知它是正确的).您应该针对 Promises/A+ 测试套件 (https://github.com/promises-aplus/promises-tests),也可以找到 Promises/A+ 规范(https://github.com/promises-aplus/promises-spec) 有助于找出算法的任何特定部分的正确行为.作为最终资源,promise 是 Promise 规范的一个非常小的实现.

N.B. I haven't tested this Promise implementation (although it is correct to the best of my knowledge). You should test any implementation you build against the Promises/A+ test suite (https://github.com/promises-aplus/promises-tests) and may also find the Promises/A+ spec (https://github.com/promises-aplus/promises-spec) useful in figuring out what the correct behavior is for any specific part of the algorithm. As a final resource, promise is a very minimal implementation of the Promise spec.

这篇关于基本的 Javascript promise 实现尝试的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆