Nolan的承诺/ A + JavaScript难题

8
我已经阅读了 Nolan Lawson 的文章 We have a problem with promises 几次,但仍有一些关于 JavaScript 中 promises 的问题。在 Nolan 文章的结尾,你可以找到四个谜题的答案(我在这里附上了截图)。
因此,我有几个问题:
  1. 为什么第一个谜题中的doSomethingElse()函数值为undefined?在我看来,它应该像第四个谜题中一样有resultOfDoSomething

  2. 第三个和第四个谜题有什么区别?在第三个谜题中,我们在第一个then中写下doSomethingElse(),而在第四个谜题中,我们只写下函数名doSomethingElse。这是怎么可能的?doSomethingElse在第四个谜题的then中到底做了什么?

  3. 为什么第三个谜题中的doSomethingElse()函数会与doSomething同时开始?

Puzzle 1 and 2 Puzzle 3 and 4

6个回答

3
  1. 为什么第一个难题中的doSomethingElse()函数会通过undefined

让我们看一个更简单的例子:

foo();   // Line #1
foo(42); // Line #2

两行代码的区别是什么?在第一行,我们调用了foo但没有传递任何值(因此为undefined),而在第二行,我们调用了foo并传递了42。
回到文章:
doSomething().then(function () {
    return doSomethingElse( );
    //                     ^-- we are not passing a value
}).then(finalHandler);

这与第四个谜题有什么不同?在第四个谜题中,我们将一个引用传递给then函数。将该函数的引用提供给Promise允许它以后使用Promise的结果调用该函数。[1]
  1. 第三和第四个谜题有什么区别?在第三个谜题中,我们写下doSomethingElse(),而在第四个谜题中我们写下了doSomethingElse

为了解释第三和第四个谜题之间的差异,让我们看一下更简单的例子。以下两行代码有何不同:

var foo = function foo() {
    return 42;
};
var a = foo;   // line #1
var b = foo(); // line #2

在上面的片段中,a将包含对函数foo的引用,而b将包含调用foo的结果(这将是42)。
  1. 为什么第三个谜题中的doSomethingElse()doSomething同时开始?

类似于第三个和第四个函数之间的区别,注意使用()调用函数。在这里,我们调用了doSomething()doSomethingElse(),而不是等待承诺“解决”(等待第一个调用完成)。让我们逐步执行:

  1. 执行doSomething返回一个Promise
  2. doSomethingElse()附加到这个Promise上 - 但是请注意,doSomethingElse()是我们调用doSomethingElse(),因此它会异步发生,我们会将其返回的Promise附加到doSomething()返回的Promise上。
  3. 将函数finalHandler附加到Promise上

2

简而言之,使你的示例易于理解的简单两个规则:

  1. .then 中返回一个值与返回以该值解决的 promise 相同。

  2. 当你没有返回任何内容时,等同于返回 undefined,进而执行第1条规则。

如果你记住这2点,你的 promise 难题将变得清晰明了。


1
doSomething().then(function () {
  return doSomethingElse();
});

Why does doSomethingElse() function in 1-st puzzle have undefined value? To my mind, it must have resultOfDoSomething like in 4-th puzzle.

doSomething的承诺结果被传递给匿名函数(该函数甚至不接受参数)。因此,无论传递什么都会丢失。而doSomethingElse在没有任何参数的情况下被调用,因此第一个函数参数是undefined

doSomething().then(doSomethingElse());

doSomething().then(doSomethingElse);

What's the difference beetwen 3-rd and 4-th puzzles? In 3-rd puzzle in first then we write doSomethingElse() and in 4-th puzzle we write here only the name of function - doSomethingElse. How is it possible? What does really doSomethingElse do in then of 4-th puzzle?

JavaScript中的函数是对象,您可以随意传递。这实际上就是整个回调传递的工作方式:有一个函数(在这里是then),它接受一个函数作为参数,稍后再调用该函数。
因此,在后面的代码中,传递给then的函数是doSomethingElse,因此稍后会调用该函数。但是在前者中,我们直接调用doSomethingElse(使用括号())。因此传递给then的是该函数的返回值,这不太可能是一次完成承诺的可评估函数。

为什么第三个谜题中的doSomethingElse()函数与doSomething同时开始?

正如我所说,该函数立即被调用,在您调用doSomething的同时调用。只有返回值被传递给then,而该值将在承诺解决后调用。

1

为什么第一个谜题中的doSomethingElse()函数值为undefined?在我看来,它必须像第四个谜题中一样有resultOfDoSomething

不是这样的。确实,resultOfDoSomething被传递给了调用then的回调函数,但我们在第一个代码中只是忽略了它:

doSomething().then(function(resultOfDoSomething) {
//               it's here: ^^^^^^^^^^^^^^^^^^^
    return doSomethingElse(  );
//          but not there: ^^ - Ooops!
});

“第三个谜题”和“第四个谜题”有什么区别?在第三个谜题中,我们首先写下了 doSomethingElse(),而在第四个谜题中,我们只写了函数名称 doSomethingElse。这是怎么回事?在第四个谜题的 then 中,doSomethingElse 到底做了什么?
如你所知,这取决于调用函数和函数本身之间的区别。
此外,我们总是希望 - 不,必须 - 向 then(…) 传递一个回调函数。以下两种方式是等效的:
promise.then(function callback(res) {
    …
});

function callback(res) {
    …
}
promise.then(callback);

这是预期的 - 你将一个函数传递给调用,所以它像往常一样链接到了 Promise。但是带有调用的 callback() 不再是一个函数:

为什么第三个谜题中的 doSomethingElse() 函数会在同一时刻开始执行,就像 doSomething 一样?

因为它立即在表达式 then(doSomethingElse()) 中被调用 - 实际上是在 then 之前调用的,作为它的参数。所以它实际上是在 doSomething() 之后的某个时刻调用的,但在同一个事件循环转中;而这两个异步操作将并行运行。

另外,then 只接受函数参数,其他所有内容都会被忽略。所以这里没有链式调用发生,这与执行以下操作相同:

var promise = doSomething();
doSomethingElse();
promise.then(finalHandler);

不是我们想要的!

顺便提一下,这些也被解释为“高级错误#3:承诺与承诺工厂”和“高级错误#5:承诺失效”,我认为这两个错误都是初学者的错误:-)


1
我认为执行顺序总是先执行 doSomethingElse,然后执行doSomething和finalHandler。这只是JavaScript语法。
区别在于传递给函数的参数不同。
这两个函数返回两个promises。这两个promises的执行顺序显示在你发布的图片中。我认为作者的意思是两个promises的顺序,而不是函数的执行顺序。

/**
 * Created by tshungle on 17-2-24.
 */
'use strict'
function doSomething() {
  console.log("doSomething start");
  sleep(1000)
  console.log("doSomething end");
  return new Promise(function (resolve, reject) {
    setTimeout(function() {
      console.log("Promise of doSomething is about to resolve");
      resolve("doSomething resolvation")
    }, 2500);
  });

}

function doSomethingElse() {
  console.log("doSomethingElse start");
  sleep(1000);
  console.log("doSomethingElse end");
  return new Promise(function (resolve, reject) {
    setTimeout(function() {
      console.log("Promise of doSomethingElse is about to resolve");
      resolve("doSomethingElse resolvation")
    }, 2500);
  });
}

function finalHandler(result) {
  console.log("finalHandler result is:", result);
}

function sleep(ms) {
  var stop = new Date().getTime();
  while(new Date().getTime() < stop + ms) {
    ;
  }
}


//1
 doSomething().then(function () {
   return doSomethingElse();
 }).then(finalHandler);

//2
// doSomething().then(function () {
//   doSomethingElse();
// }).then(finalHandler);
//
//3
// doSomething().then(doSomethingElse())
//   .then(finalHandler);

//4
// doSomething().then(doSomethingElse)
//   .then(finalHandler);


1
你必须注意:doSomethingElse 是一个函数引用,而 doSomethingElse() 表示你立即“调用”该函数。在这里,如果有的话,你可以获得该函数的结果...

关于承诺,我理解:

A) “then” 方法将前一次调用的结果作为参数传递给自己的参数(如果它是一个函数)。

B) 只有当“then”方法内部的函数返回某些内容时,执行才会被延迟。

在这些情况下,我们有:

难题1)resultOfDoSomething 作为匿名函数的参数使用。

function (){ return doSomethingElse ();} 

这将不带参数调用doSomethingElse()

谜题2)匿名函数不返回数据,因此立即执行下一个“then”。

谜题3)表达式doSomethingElse()是调用doSomethingElse的结果。该值用作“then”方法的参数,并且由于它不是函数(在此示例中!),因此我们可以传递给其他“then”...

finalHandler以最后一个有效结果作为参数,本例中为resultOfDoSomething。

谜题4)每个“then”方法都接收一个函数的引用作为参数。这些函数中的每一个都将使用先前的结果作为参数进行调用。因此,每个函数都必须等待上一次执行的结果... 这就是承诺队列...


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接