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

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

问题描述

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

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

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

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

});

所以现在可以创建,调用和解析承诺。每个然后方法将返回新的Aaa(Promise),以便这些可以链接。现在,下面的代码使用上面创建的promise和chain 然后回调。每个然后返回新的承诺,在这种情况下它似乎工作正常:

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 

然而问题是当其中一个然后调用返回一个承诺:

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 的调用不应该被调用UNTIL我们刚刚添加的返回的promise已经解决。

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

如何才能最好地实施?

How would that be best implemented?

推荐答案

有很多案例你没有在这里处理。最好的办法是首先将承诺建立为状态机:

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

现在让我们定义一个简单的助手来完成其余的实现: / p>

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 (result && (typeof result === 'object' || typeof result === '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;
  }
}

注意如何解决可以接收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只能被实现/拒绝一次。我们还有第三方承诺可能行为不端的问题,我们应该保护我们的代码。出于这个原因,我还没有从 resolve 中调用 result.then(解析,拒绝)。相反,我把它拆分成一个单独的函数:

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 可能会多次调用 resolve 拒绝,以及它可能会引发错误。我们需要处理所有这些情况(这就是 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并且不处理 onFulfilled onRejected抛出的错误

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的 .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 接受承诺并等待其解决。

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.

NB 我没有没有测试过这个Promise的实现(尽管据我所知,它是正确的)。您应该测试针对Promises / A +测试套件构建的任何实现( https://github.com/ promises-aplus / promises-tests )也可以找到Promises / A +规范( https ://github.com/promises-aplus/promises-spec )用于确定算法的任何特定部分的正确行为。作为最终资源,承诺是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承诺实现尝试的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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