Promises - 如何在没有 async / await 的情况下使异步代码同步执行?

jav*_*ing 4 javascript asynchronous promise async-await

  var p1 = new Promise(function(resolve, reject) {  
    setTimeout(() => resolve("first"), 5000);
  });
  var p2 = new Promise(function(resolve, reject) {  
    setTimeout(() => resolve("second"), 2000);
  });
  var p3 = new Promise(function(resolve, reject) {  
    setTimeout(() => resolve("third"), 1000);
  });

  console.log("last to print");

p1.then(()=>p2).then(()=>p3).then(()=> console.log("last to be printed"))
Run Code Online (Sandbox Code Playgroud)

当我阅读有关承诺时,我知道当我使用 async /await 时,我可以同步打印承诺(在这种情况下是打印:第一个、第二个、第三个、最后一个打印)。现在我也读到同样的事情可以使用 .then 链接来实现,而 async/await 并不是什么“特别”的东西。然而,当我尝试链接我的承诺时,除了“最后打印”的 console.log 之外,什么也没有发生。任何见解都会很棒!谢谢!!

编辑质疑:

  var p1 = new Promise(function (resolve, reject) {
    setTimeout(() => console.log("first"), 5000);
    resolve("first resolved")
  });
  var p2 = new Promise(function (resolve, reject) {
    setTimeout(() => console.log("second"), 2000);
    resolve("second resolved")
  });
  var p3 = new Promise(function (resolve, reject) {
    setTimeout(() => console.log("third"), 0);
    resolve("third resolved")
  });

  console.log("starting");
  p1.then((val) => {
    console.log("(1)", val)
    return p2
  }).then((val) => {
    console.log("(2)", val)
    return p3
  }).then((val) => {
    console.log("(3)", val)
  })
Run Code Online (Sandbox Code Playgroud)

日志:

starting
(1) first resolved
(2) second resolved
(3) third resolved
third
second
first
Run Code Online (Sandbox Code Playgroud)

1:如果传递给 new Promise 的 executor 函数是立即执行的,在新的 Promise 返回之前,那么为什么这里的 promise 先()同步)在 setTimeouts(异步)执行之后呢?

  1. 返回值与解决承诺:

    var sync = function () { return new Promise(function(resolve, reject){ setTimeout(()=> { console.log("start") resolve("hello") //--works // return "hello" //--> 什么都不做 }, 3000); }) } sync().then((val)=> console.log("val", val))

T.J*_*der 7

在返回新承诺之前,您传递给的 executor 函数会立即new Promise执行。所以当你这样做时:

var p1 = new Promise(function(resolve, reject) {  
  setTimeout(() => resolve("first"), 5000);
});
Run Code Online (Sandbox Code Playgroud)

...受许被分配到的时间p1,则setTimeout已经被调用,定于5秒后再回调。无论您是否侦听 promise 的解析,都会发生该回调,无论您是通过await关键字还是then方法侦听解析都会发生该回调。

所以你的代码setTimeouts 立即开始三个,然后开始等待第一个承诺的解决,然后才等待第二个承诺的解决(它已经解决了,所以这几乎是立即的),然后等待第三个(再次相同) .

要让您的代码setTimeout仅在前一个超时完成时按顺序执行这些调用,您必须在前一个 Promise 解决之前不要创建新的 Promise(使用较短的超时以避免大量等待):

var p1 = new Promise(function(resolve, reject) {  
  setTimeout(() => resolve("first"), 5000);
});
Run Code Online (Sandbox Code Playgroud)

请记住,promise 不会做任何事情,也不会改变promise executor 中代码的性质。所有的承诺确实是提供观察的东西(有非常方便的组合语义)结果的一种手段。

让我们将这三个 promise 的共同部分分解为一个函数:

function delay(ms, ...args) {
    return new Promise(resolve => {
        setTimeout(resolve, ms, ...args);
    });
}
Run Code Online (Sandbox Code Playgroud)

然后代码变得清晰一点:

console.log("starting");
new Promise(function(resolve, reject) {  
  setTimeout(() => resolve("first"), 1000);
})
.then(result => {
    console.log("(1) got " + result);
    return new Promise(function(resolve, reject) {  
      setTimeout(() => resolve("second"), 500);
    });
})
.then(result => {
    console.log("(2) got " + result);
    return new Promise(function(resolve, reject) {  
      setTimeout(() => resolve("third"), 100);
    });
})
.then(result => {
    console.log("(3) got " + result);
    console.log("last to print");
});
Run Code Online (Sandbox Code Playgroud)

现在,让我们把它放在一个async函数中并使用await

function delay(ms, ...args) {
    return new Promise(resolve => {
        setTimeout(resolve, ms, ...args);
    });
}
Run Code Online (Sandbox Code Playgroud)

通过标准化我们观察异步过程的方式,Promise 使这种语法成为可能。


重新编辑:

1:如果传递给 new Promise 的 executor 函数是立即执行的,在新的 Promise 返回之前,那么为什么这里的 Promise 是先()同步),后setTimeouts(异步)执行?

这个问题有两个部分:

A)“......为什么这里承诺首先解决()同步)......”

B)“......为什么在这里承诺解决......在setTimeouts(异步)执行之后”

(A) 的答案是:尽管您同步解析它们,但then 始终异步调用其回调。这是承诺提供的保证之一。您正在p1执行程序函数返回之前解决(在该编辑中)。不过顺便说一下你观察的分辨率确保你看到的决议,以便,因为你不开始观察p2,直到p1解决了,然后你不开始观察p3,直到p2得到解决。

(B) 的答案是:它们没有,您正在同步解决它们,然后异步观察这些解决方案,并且由于它们已经解决,因此发生得非常快;稍后,计时器回调运行。让我们看看您如何p1在该编辑中创建:

var p1 = new Promise(function (resolve, reject) {
  setTimeout(() => console.log("first"), 5000);
  resolve("first resolved")
});
Run Code Online (Sandbox Code Playgroud)

发生的事情是:

  1. new Promise 被调用
  2. 它调用执行器函数
  3. 执行器函数调用setTimeout调度回调
  4. 你立即解决了承诺 "first resolved"
  5. new Promise 返回并将已解决的承诺分配给 p1
  6. 稍后,超时发生并且您输出"first"到控制台

然后你做:

p1.then((val) => {
  console.log("(1)", val)
  return p2
})
// ...
Run Code Online (Sandbox Code Playgroud)

因为then总是异步调用它的回调,所以异步发生——但很快,因为承诺已经解决。

因此,当您运行该代码时,您会看到所有三个 Promise第一个setTimeout回调发生之前都已解决——因为这些 Promise 不会等待setTimeout回调发生。

您可能想知道为什么then在您"third"在控制台中看到之前看到您的最终回调运行,因为 Promise 解决方案和console.log("third")Promise 都是异步发生的,但很快(因为它是 asetTimeout(..., 0)并且所有承诺都是预先解决的):答案是那个承诺分辨率是任务setTimeout调用是宏任务(或只是“任务”)。任务调度的所有微任务在该任务完成后立即运行(并且它们调度的任何微任务也将被执行),然后从任务队列中取出下一个任务。所以运行你的脚本的任务是这样的:

  1. setTimeout回调安排任务
  2. 安排一个微任务来调用p1then回调
  3. 当任务结束时,它的微任务被处理:
    1. then运行第一个处理程序,调度一个微任务来运行第二个then处理程序
    2. 第二个then处理程序运行并调度一个微任务来调用第三个then处理程序
    3. 等等,直到所有then处理程序都运行
  4. 从任务队列中选取下一个任务。这可能是 的setTimeout回调p3,因此它会运行并"third"出现在控制台中
  1. 返回值与解决承诺:

您在问题中提出的部分对我来说没有意义,但您对此的评论确实如此:

我读到返回一个值或解决一个承诺是一样的......

什么你可能读过的是返回一个值thencatch相同返回解决承诺thencatch。这是因为thencatch创建并返回新的承诺,他们是所谓的时候,如果它们的回调函数返回一个简单的(非承诺)的价值,他们解决他们与价值创造的承诺; 如果回调返回一个承诺,他们会根据该承诺是解决还是拒绝来解决或拒绝他们创建的承诺。

所以例如:

.then(() => {
    return 42;
})
Run Code Online (Sandbox Code Playgroud)

.then(() => {
    return new Promise(resolve => resolve(42));
})
Run Code Online (Sandbox Code Playgroud)

具有相同的最终结果(但第二个效率较低)。

thenorcatch回调中:

  1. 返回非承诺解决的承诺then/catch与价值创造
  2. 抛出错误 ( throw ...) 拒绝使用你抛出的值的承诺
  3. 返回一个 promise根据回调返回的 promise 使then/catch的 promise 解决或拒绝


Que*_*tin 6

您不能使异步代码同步执行。

即便async/await只是语法,让您同步式的控制流程的承诺。

然而,当我尝试链接我的承诺时,除了“最后打印”的 console.log 之外,什么也没有发生。任何见解都会很棒!

其他函数不生成任何输出。这与他们在承诺中无关。

您启动三个计时器(全部同时),然后记录“最后打印”,然后链接一些承诺,以便在所有三个承诺都解决时(在您开始所有承诺后 5 秒)打印“最后打印”。

如果您希望计时器按顺序运行,那么您必须仅在前一个计时器完成时才启动它们,如果您想查看它们解决的问题,那么您必须编写实际查看它的代码。

function p1() {
  return new Promise(function(resolve, reject) {
    setTimeout(() => resolve("first"), 5000);
  });
}

function p2() {
  return new Promise(function(resolve, reject) {
    setTimeout(() => resolve("second"), 2000);
  });
}

function p3() {
  return new Promise(function(resolve, reject) {
    setTimeout(() => resolve("third"), 1000);
  });
}

function log(value) {
  console.log("Previous promise resolved with " + value);
}

p1()
  .then(log)
  .then(p2)
  .then(log)
  .then(p3)
  .then(log)
  .then(() => console.log("last to be printed"));
Run Code Online (Sandbox Code Playgroud)

Async/await 可以说更简洁:

function p1() {
  return new Promise(function(resolve, reject) {
    setTimeout(() => resolve("first"), 5000);
  });
}

function p2() {
  return new Promise(function(resolve, reject) {
    setTimeout(() => resolve("second"), 2000);
  });
}

function p3() {
  return new Promise(function(resolve, reject) {
    setTimeout(() => resolve("third"), 1000);
  });
}

function log(value) {
  console.log("Previous promise resolved with " + value);
}

(async function() {
  log(await p1());
  log(await p2());
  log(await p3());
  console.log("last to be printed");
}());
Run Code Online (Sandbox Code Playgroud)