JavaScript承诺如何执行其代码?

时间:2018-07-14 22:47:14

标签: javascript callback promise

这是我从https://scotch.io/tutorials/javascript-promises-for-dummies窃取的代码,并做了一些修改。

var momIsHappy = false;

var getAPhone = function (whatToDoIfPhoneIsPurchased,
                          whatToDoIfPhoneIsNotPurchased) {
  fulfillPromiseToPurchasePhone = whatToDoIfPhoneIsPurchased;
  breakPromiseToPurchasePhone = whatToDoIfPhoneIsNotPurchased;

  if (momIsHappy) {
      var phone = {
          brand: 'Samsung',
          color: 'black'
      };
      fulfillPromiseToPurchasePhone(phone); // fulfilled
  } else {
      var reason = new Error('mom is not happy');
      breakPromiseToPurchasePhone(reason); // reject
  }
}

var aPhoneIMightGet = new Promise(getAPhone);

var playWithNewPhone = function () {
    findOutIfIGetPhone = aPhoneIMightGet;

    findOutIfIGetPhone
    .then(function (aPhone) {
        // yay, you got a new phone
        console.log(aPhone);
     // output: { brand: 'Samsung', color: 'black' }
    })
    .catch(function (error) {
        // oops, mom don't buy it
        console.log(error.message);
     // output: 'mom is not happy'
    });
};

我不明白的是:getAPhone何时被调用?我没打电话谁叫的什么时候?怎么样?

3 个答案:

答案 0 :(得分:2)

让我们备份一下并描述Promise构造函数的作用。 Promise构造函数传递给一个同步调用的回调。该回调(通常称为“ Promise executor回调”)背后的想法是,它将启动一些异步操作,然后立即返回。稍后,当异步操作完成后,它将解决或拒绝该诺言。

这是一个简单的示例,我们可以逐步进行操作(您可以在下面的代码段中运行此示例以查看日志记录顺序):

console.log("0");
let p = new Promise(function(resolve, reject) {
    console.log("1");
    setTimeout(function() {
        console.log("2");
        resolve("done");
    }, 1000);
}).then(function(val) {
    console.log("3");
});
console.log("4");

您将获得以下输出:

0     // start of the code
1     // Promise executor callback called and setTimeout() initiated
4     // Promise created and now initialized
2     // setTimeout fires
3     // Promise gets resolved and .then() handler called

而且,这是发生的事件的顺序:

  1. console.log("0");运行
  2. 您调用new Promise()并将其传递给回调函数
  3. Promise构造函数运行,创建一个Promise对象并同步调用回调函数
  4. 回调函数的目的是启动一些非阻塞的异步操作(在本例中为简单的setTimeout()),但它可能是更复杂的操作,例如执行一些文件操作,搜索数据库等...
  5. 向回调函数传递了两个参数,每个参数都是一个函数。当回调中启动的异步操作完成时,回调应调用这两个函数之一。如果这是典型的异步操作,则回调将启动异步操作,然后返回。然后,在某个时间之后,将调用与异步操作相关的另一个回调,然后根据该操作是成功还是返回错误,该回调将调用传递给该回调的两个函数之一(我将其命名为{{1 }}和resolve。您的示例选择了更长的名称。
  6. 在我的代码示例中,Promise回调(称为Promise执行程序)立即登录到控制台,然后调用reject。由于setTimeout()是非阻塞且异步的,因此它将启动计时器并立即返回。此时,Promise执行程序回调已完成并返回。
  7. 计时器现在正在运行,promise执行程序回调已运行并返回。 promise构造函数完成后,它返回新的Promise对象。
  8. 然后,调用promise上的setTimeout()方法。这会为promise注册一个完成回调,然后立即返回。从技术上讲,它返回第二个Promise,该诺言链接到使用Promise构造函数创建的第一个Promise。已注册的此.then()回调函数已保存,但尚未调用。
  9. 现在,此示例中的最后一行代码将执行,并将.then()记录到控制台。
  10. 此代码已完成(目前)。紧随其后的任何其他代码将立即执行,或者如果没有其他代码,则Javascript解释器将返回事件循环并等待下一个事件发生。
  11. 有时,计时器会触发。它将事件插入到Javascript事件队列中,当解释器完成其他工作后,再从事件队列中获取下一个事件,它将调用传递给4的回调。
  12. 执行该setTimeout()回调时,它将记录setTimeout(),然后调用2。调用resolve("done")可以解决第一个诺言,并告诉它随后触发任何resolve()处理程序并调用它们。
  13. 这时,将调用传递到.then()的回调并记录.then(),一切都已完成。

现在要回答有关您的代码示例的一些具体问题:

  

我不明白的是:什么时候叫getAPhone?我没打电话谁叫的什么时候?怎么样?

您将3传递给Promise构造函数。 Promise构造函数将其作为运行构造函数以创建新Promise对象的一部分进行调用。

这是一个简单的例子:

getAPhone

调用doIt时,会向其传递函数引用(本质上是指向函数的指针)。这样,您要调用的函数就可以在调用函数时使用其想要的任何参数来调用该函数。在这种情况下,function doIt(someCallback) { someCallback("hi"); } function myCallback(greeting) { console.log(greeting); } doIt(myCallback); // logs "greeting" 希望您向其传递一个函数引用,它将立即调用该函数并将其传递给doIt()

  

那么,为什么promise构造函数在运行我的函数时不仅仅阻塞我的代码?如果构造函数正在做的就是立即为我调用一个函数,为什么它与直接调用该函数有何不同?

promise构造函数是同步运行的(当您调用"hi"时)。 Promise构造函数同步运行Promise执行程序回调函数。但是,它通常所做的只是启动异步和非阻塞操作,然后立即返回。然后,异步操作会在后台运行,并在将来完成时触发它自己的回调。

  

那么,为什么Promise的构造函数中没有代码块?它不是像其他所有函数一样在调用堆栈上起作用吗?

Promise构造函数正在阻塞。它直到完成才返回。像其他所有函数一样,它是调用堆栈上的函数。

在这里可能使您感到困惑的是,您的new Promise()示例不包含任何异步代码。这是完全同步的。完全没有理由在该代码中使用promises。

以下是该函数的简化版本:

getAPhone()

所有这些操作就是检查var momIsHappy = false; var getAPhone = function (resolve, reject) { if (momIsHappy) { var phone = { brand: 'Samsung', color: 'black' }; resolve(phone); // fulfilled } else { var reason = new Error('mom is not happy'); reject(reason); // reject } } ,然后立即解决或拒绝诺言。这里没有异步操作。 您完全没有理由在示例中使用promise。 Promise是用于管理和协调异步操作的工具。如果您没有任何异步操作,则不应使用它们,因为当可以使用普通和更简单的函数调用时,它们会增加同步代码的复杂性。使用异步操作,可能难以协调和管理异步操作以及错误处理,尤其是当有多个异步操作需要排序或协调并且处理所有错误时,尤其如此。这就是诺言的目的。

因此,在您的特定情况下,您可以将momIsHappy设置为常规函数,直接调用它而根本不使用promises。

答案 1 :(得分:0)

用一个简单的例子来测试就不难了:

function someFun() {
    console.log("testing")
}
let p = new Promise(someFun)

因此,即使我们对诺言不做任何事情,当您做出诺言并同步运行时,诺言构造函数仍会调用该函数。在更现实的情况下,您可以向其传递一个函数,该函数带有async回调,该回调在异步操作完成时执行,但是传递给new Promise()的函数仍然会立即触发:

function someFun(resolve) {
    console.log("someFun called")
    setTimeout(() => resolve("promise resolved"), 1500)
}
console.log("starting")

let p = new Promise(someFun)
.then(console.log)

console.log("just called the promise")

答案 2 :(得分:0)

  

我不明白的是:什么时候调用“ getAPhone”?我没打电话   它。谁叫的什么时候?怎么样?

也许更直接地回答此问题:当您将新的Promise分配给变量“ aPhoneIMightGet”时,构造函数将按照上面的Mark Meyer的说明运行。

您的“ getAPhone”是传递给Promise构造函数的“执行程序功能”。这样,它会在Promise构造函数返回之前立即执行。这就是所谓的“ getAPhone”功能。

可以找到here的有用文章。