了解 setTimeout 及其在 JavaScript Promises 中的实现

Understanding setTimeout and its implementation in JavaScript Promises

我有以下代码:

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 语句时,我们 return 对象并有效地停止函数?如果是这样,那么程序如何到达 setTimeout?

代码片段中的代码确实解决了这个问题,但是,根据 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 中 Promises 的概念,我再举一个例子:

假设我想创建一个程序,上面写着 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 响应或者在使用 WebAPIs 等时

P.S: setTimeout() 只是一个例子。 在写一个时一定要使用 setTimeout() 并不是一个规则Promise。如果您使用 WebAPI 获取数据,您通常会写 $.ajax(...).then(...)fetch(...).then(...) 而不是 setTimeout()

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

  1. 不符合规则2.1.2.2

    When fulfilled, a promise must have a value, which must not change.

    ...也不符合规则 2.2.2.3:

    If onFulfilled is a function it must not be called more than once.

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

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

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

  2. 不符合规则2.2.6:

    then may be called multiple times on the same promise.

    • If/when promise is fulfilled, all respective onFulfilled callbacks must execute in the order of their originating calls to 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 逐步说明。

关于你的问题

how does the program even get to the 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 是用上面的回调函数初始化的。然后你的主代码的回调函数 调用 resolve 带有一个字符串参数。由于 resolve 是上面的回调函数,我们可以看到它被执行时 message 初始化为“新消息”。该函数执行setTimeout.