【问题标题】:Understanding setTimeout and its implementation in JavaScript Promises了解 setTimeout 及其在 JavaScript Promises 中的实现
【发布时间】:2021-05-09 03:37:33
【问题描述】:

我有以下代码:

function MyPromise(configFunction) {
  let nextSuccessCallBack = undefined;
  let nextResolve = undefined;
  configFunction(function(message){
    setTimeout(function(){
      if(nextSuccessCallBack) {
        var result = nextSuccessCallBack(message);
        if(result && result.then) {
          result.then(nextResolve);
        } else {
          nextResolve && nextResolve(result);
        }
      }
    })
  });

  return {
    then: function(successCallback) {
      nextSuccessCallBack = successCallback;
      return new MyPromise(function(resolve) {
        nextResolve = resolve;
      })
    }
  }
}

new MyPromise(function(resolve, reject) {
  resolve('new message');
}).then(function(message) {
  console.log(message);
  return 'another message'
}).then(function(message) {
  console.log(message)
  console.log('here')
})

在这个例子中,nextSuccessCallBack 似乎被设置为.then 中的回调函数,然后它在 setTimeout 函数中的值现在被填充。然而,这让我很困惑。我想当我们到达构造函数中的return语句时,我们返回对象并有效地停止函数?如果是这种情况,那么程序如何到达setTimeout?

【问题讨论】:

  • configFunction被调用(这是传递给MyPromise的回调),它调用resolve(这是传递给configFunction的回调),它调用setTimeout。
  • 这是一个非常糟糕的Promise 实现。我根本不像一个真正的承诺那样工作。

标签: javascript promise settimeout es6-promise


【解决方案1】:

这不是一个正确的 Promise 实现。它显然没有拒绝的能力,而且对于实现的履行功能,它也不符合Promises/A+ specification。举两个例子:

  1. 不符合规则 2.1.2.2

    当实现时,promise 必须有一个值,该值不能改变。

    ....也不符合规则 2.2.2.3:

    如果onFulfilled 是一个函数,则不能多次调用它。

    在您的实现中,如果您要向resolve 添加第二个调用:

    new MyPromise(function(resolve, reject) {
      resolve('new message'); resolve('new message2');
    }).then((function(message) {
      console.log(message);
      // ... etc
    })
    

    ...那么对resolve 的两个调用都会触发then 回调,并显示承诺的值在第一次设置后被修改。这完全违背了 Promise 的原则:Promise 只能解决一次。

  2. 不符合规则2.2.6:

    then 可以在同一个 Promise 上被多次调用。

    • 如果/当满足promise 时,所有相应的onFulfilled 回调必须按照它们对then 的发起调用的顺序执行。

    如果我们使用以下代码,则不会发生这种情况:

    let p = new MyPromise(resolve => resolve("value"));
    p.then(console.log); // This callback is called
    p.then(console.log); // This callback is not called -> violation!
    

这些都是基本的缺点,而这只是冰山一角。

如果您对如何按照 Promise/A+ 实施它感兴趣,请查看 at one I did a few years ago 并提供分步说明。

关于你的问题

程序是如何到达setTimeout的?

当你的主代码执行这个时:

new MyPromise(function(resolve, reject) {
  resolve('new message');
})

...然后使用该回调函数初始化参数变量configFunction。 MyPromise 构造函数调用它,将以下回调作为第一个参数传递给它:

  function(message){
    setTimeout(function(){
      if(nextSuccessCallBack) {
        var result = nextSuccessCallBack(message);
        if(result && result.then) {
          result.then(nextResolve);
        } else {
          nextResolve && nextResolve(result);
        }
      }
    })
  }

这意味着你的主代码的回调函数中的resolve 是用上面的回调函数初始化的。然后你的主代码的回调函数 calls resolve 带有一个字符串参数。由于resolve 是上面的回调函数,我们可以看到它在message 初始化为“新消息”的情况下执行。该函数执行setTimeout。

【讨论】:

    【解决方案2】:

    sn-p 中的代码确实解决了这个目的,但它令人困惑,因为根据 MDN Uses Promises in JavaScript 的方式对 Promise 的错误实现是为了更好的代码效率和理解。

    上面的代码也可以这样写:

    const examplePromise = new Promise((resolve, reject) => {
        resolve("Another Message");
    });
    
    console.log("New Message");
    examplePromise.then((message) => {
        console.log(message);
    });
    

    现在,为了更好地理解 JavaScript 中的 Promise 概念,我再举一个例子:

    假设我想创建一个程序,它会说 hello,然后在 2 秒后说 How Are You?

    有两种方法可以解决这个问题

    • 仅使用setTimeout()函数,该函数在一定时间后执行函数
    • 通过setTimeout() 使用承诺

    案例一(使用setTimeout()):

    console.log("hello"); //Saying hello for the first time
    
    setTimeout(function () {
        console.log("How Are You?");
    }, 2000); //2000 milliseconds is 2 seconds
    

    案例二(使用Promises)

    console.log("hello");
    
    const example = new Promise((resolve) => {
        setTimeout(() => {
            resolve("How Are You?");
        }, 2000);
    });
    
    example.then((message) => {
        console.log(message);
    });
    

    现在,对于简单的情况,例如在 2 秒后记录一条消息不需要 Promises。您可以使用它们(没问题),但实际上,Promises 用于以下情况:您等待函数执行数据库查询,或者您需要等待服务器给您一些 HTML 响应或使用 WebAPI 等.

    P.S:setTimeout() 只是一个例子。 写Promise 时,您不必总是使用setTimeout()。如果您使用 WebAPI 获取数据,您通常编写 $.ajax(...).then(...) 或 fetch(...).then(...) 而不是 setTimeout()

    【讨论】:

    • 什么是“JavaScript 编程指南”?另外,我认为 OP 试图理解为什么 他们的 算法不起作用,而不是如何使用原生算法......
    • ...它不会迷路,还是?您可以根据需要起草您的问题/答案,而不会丢失它。 SO 也将其保存到本地存储...
    猜你喜欢
    • 2020-08-19
    • 2016-10-03
    • 1970-01-01
    • 1970-01-01
    • 2014-09-24
    • 2014-05-18
    • 2014-07-11
    • 1970-01-01
    相关资源
    最近更新 更多