Promises - How to make asynchronous code execute synchronous without async / await?

时间:2019-01-07 14:02:52

标签: 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"))

As I was reading about promises, I know that I can print promises synchronous (in this case print: first, second, third, last to print) when I use async /await. Now I have also been reading that the same thing can be achieved using .then chaining and async/await is nothing 'special'. When I try to chain my promises, however, nothing happens except for the console.log of "last to be printed". Any insight would be great! Thanks!!

Edit to question:

  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)
  })

Loggs:

starting
(1) first resolved
(2) second resolved
(3) third resolved
third
second
first

1: if executor function passed to new Promise is executed immediately, before the new promise is returned, then why are here promises resolved ()synchronously) first and after the setTimeouts (asynchronously) gets executed?

  1. Return value vs. resolve promise:

    var sync = function () { return new Promise(function(resolve, reject){ setTimeout(()=> { console.log("start") resolve("hello") //--works // return "hello" //--> doesnt do anything }, 3000); }) } sync().then((val)=> console.log("val", val))

3 个答案:

答案 0 :(得分:4)

You cannot make asynchronous code execute synchronously.

Even async / await are just syntax that gives you a synchronous-style control flow inside a promise.

When I try to chain my promises, however, nothing happens except for the console.log of "last to be printed". Any insight would be great!

The other functions don't generate any output. That has nothing to do with them being in promises.

You start three timers (all at the same time), then log 'last to print', then chain some promises so that 'last to be printed' will print when all three promises resolve (5 seconds after you start them all going).

If you want the timers to run sequentially, then you have to initiate them only when the previous one has finished, and if you want to see what they resolve with then you have to write code that actually looks at that.

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"));

Async/await is, arguably, neater:

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");
}());

答案 1 :(得分:2)

您传递给new Promise的执行程序函数将在返回新的承诺之前立即立即执行。因此,当您这样做时:

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

...在将承诺分配给p1时,setTimeout被调用,并将回调计划了五秒钟。无论您是否侦听承诺的解决方案,都会发生该回调,并且无论您通过await关键字还是then方法来侦听解决方案,都会发生该回调。

因此,您的代码立即开始三个setTimeouts ,然后开始等待第一个诺言的解决方案,然后再等待第二个诺言的解决方案(它已经解决了,所以几乎是立即生效的),然后等待第三个(再次相同)。

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

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");
});

请记住,promise不会做任何事情,并且不会更改promise执行器中代码的性质。 所有的承诺是提供一种观察事物结果的方法(具有非常方便的可组合语义)。

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

function delay(ms, ...args) {
    return new Promise(resolve => {
        setTimeout(resolve, ms, ...args);
    });
}

然后代码变得更加清晰:

function delay(ms, ...args) {
    return new Promise(resolve => {
        setTimeout(resolve, ms, ...args);
    });
}

console.log("starting");
delay(1000, "first")
.then(result => {
    console.log("(1) got " + result);
    return delay(500, "second");
})
.then(result => {
    console.log("(2) got " + result);
    return delay(100, "third");
})
.then(result => {
    console.log("(3) got " + result);
    console.log("last to print");
});

现在,我们将其放在async函数中并使用await

function delay(ms, ...args) {
    return new Promise(resolve => {
        setTimeout(resolve, ms, ...args);
    });
}

(async() => {
    console.log("starting");
    console.log("(1) got " + await delay(1000, "first"));
    console.log("(2) got " + await delay(500, "second"));
    console.log("(3) got " + await delay(100, "third"));
    console.log("last to print");
})();

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


重新编辑:

  

1:如果传递给新Promise的执行程序函数在返回新的Promise之前立即执行,那么为什么在这里setTimeouts首先(异步)执行(异步)解析Promise? >

这个问题有两个部分:

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")
});

发生了什么事

  1. new Promise被呼叫
  2. 它调用执行程序函数
  3. 执行程序函数调用setTimeout安排回调
  4. 您立即使用"first resolved"来兑现诺言
  5. new Promise返回,并将已解决的承诺分配给p1
  6. 稍后,发生超时,您将"first"输出到控制台

然后再做:

p1.then((val) => {
  console.log("(1)", val)
  return p2
})
// ...

由于then始终以异步方式调用其回调,因此异步发生-但很快,因为承诺已经解决。

因此,当您运行该代码时,您会看到所有三个承诺在第一个setTimeout回调发生之前之前解决-因为承诺没有等待setTimeout回调到发生。

您可能想知道为什么在控制台中看到then之前,您会看到最终的"third"回调运行,因为promise决议和console.log("third")都是异步发生的,但是很快(因为它是setTimeout(..., 0),并且承诺都已预先解决):答案是,承诺解决方案是 microtasks ,而setTimeout调用是宏任务(或仅仅是“任务”)。任务调度的所有微任务在任务完成后立即运行(并且它们调度的所有微任务也将被执行),然后再从任务队列中提取下一个任务。因此,运行脚本的任务会执行以下操作:

  1. 安排setTimeout回调的任务
  2. 安排微任务来调用p1的{​​{1}}回调
  3. 任务结束时,将处理其微任务:
    1. 第一个then处理程序运行,调度一个微任务以运行第二个then处理程序
    2. 第二个then处理程序运行并计划一个微任务,以调用第三个then处理程序
    3. 等等直到所有then处理程序都已运行
  4. 从任务队列中提取下一个任务。可能是then的{​​{1}}回调,所以它开始运行并且setTimeout出现在控制台中
  
      
  1. 回报价值与解决承诺:
  2.   

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

  

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

您可能已经读到,从p3"third" 返回值与从then返回已解决的承诺相同或catch 。这是因为thencatch会在被调用时创建并返回新的诺言,并且如果其回调返回简单(非承诺)值,则它们会解析他们的诺言用那个价值创造;如果回调函数返回了一个Promise,则他们会根据该Promise是拒绝还是拒绝来解决或拒绝创建的Promise。

例如:

then

catch

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

.then(() => { return 42; }) .then(() => { return new Promise(resolve => resolve(42)); }) 回调中:

  1. 返回不承诺将解决使用该值创建的承诺then / catch
  2. 抛出错误(then)会以您抛出的值拒绝该诺言
  3. 返回承诺使catch / throw ...的承诺根据回调返回的承诺而得到解决或拒绝

答案 2 :(得分:0)

如果您需要调用await,但是包含该await的函数不必是异步的,例如因为您需要“数字”而不是“承诺数字”,则可以执行下一步:

var ex: number = new Number(async resolve => {
              var f = await funcionExample();                  
              resolve(f);
            }).valueOf();