【问题标题】:How the promise.then mechanisem works?promise.then 机制如何工作?
【发布时间】:2021-04-10 11:56:11
【问题描述】:

观看视频并阅读 [Promise.then][1] 后,我仍然不明白它是如何工作的。 所以我写了一个例子,并打印到控制台仍然输出我无法理解它是如何工作的。 我希望我的问题足够清楚,如果没有,请告诉我,我会根据您的回答尝试详细说明。 这是我的代码:

   const getCountryData = function (country) {

   fetch(`https://restcountries.eu/rest/v2/name/${country}`)
     .then(response => {
       console.log('111111111111111');
       if (!response.ok)
         throw new Error(`Country not found (${response.status})`);
       return response.json();
       })
     .then(data => {
         console.log('22222222222');
        const neighbour = data[0].borders[0];
       if (!neighbour) return;
       return fetch(`https://restcountries.eu/rest/v2/alpha/${neighbour}`);
     })
     .then(response => {
         console.log('333333333')
       if (!response.ok)
         throw new Error(`Country not found (${response.status})`);
       return response.json();
        })
     .then(data => console.log('44444444444'))
     .catch(err => {
       console.error(`${err} ????????????`);
     })
     .finally(() => {
       console.log('finalyyyy')
     });
     
     console.log("after all async calls");
 };

我无法理解以下内容:

  1. 如果我们在第一个 then 上查找示例,它会获取一个回调作为参数,该回调返回一个 Promise 对象。哪个对象调用第二个then?是从第一次调用 when 时返回的还是从回调中返回的?
  2. 我知道回调是“附加”到 fetch 函数的。例如,当 fetch 函数完成时,回调将被执行。 但是我不明白第二个、第三个和第四个 then 的调用方式和时间。第一个回调还包含一个异步函数** response.json()**,所以如果他需要 second when 将如何以及何时启动json.response 从第一个 when?
  3. 如果第一次或第二次发生异常,代码如何知道跳转到catch函数?
    [1]:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then

【问题讨论】:

  • "然后哪个对象调用第二个?" - 没有“对象”调用它。 您的代码 在一个对象上调用该方法 - 在您的代码中,这是第一个 .then() 调用返回的承诺。 "已经从回调中返回的那个?" - 调用第二个.then()时回调还没有执行,所以它不能是返回值
  • 我阅读了两个链接,但仍然无法理解一些内容。 1.是否所有when方法都立即执行,无需等待回调先运行? 2.第一个回调返回的值是第二个回调输入(不完全是因为它从'Promise'中取出值)?
  • 这是实现承诺的值,通常传递给承诺的resolve() 函数。这取决于如何构建承诺。如果它是由(返回).then(res => …) 调用构造的承诺,是的,它通过回调的返回值解决。但是,resolving means that promises are recursively unwrapped,所以如果你从 then 回调返回一个承诺,外部承诺最终将通过内部承诺的 result 实现。

标签: javascript asynchronous promise


【解决方案1】:

then 函数不接收回调,它们接收前一个异步操作的结果。 所以fetch的结果被传递给第一个thenresponse.json()的结果被传递给第二个then等等......

如果发生错误,链会中断,它会直接转到第一个 catch 函数。

实际上还有更多,考虑到then 函数中的 fetch 和其他操作可以是异步的,所以在运行时发生的情况是 fetch 函数的结果被推送到事件队列中,当事件循环将您的下一个 then 函数从队列中提取并传递给该函数。
本文解释了它在内部是如何工作的:https://www.digitalocean.com/community/tutorials/understanding-the-event-loop-callbacks-promises-and-async-await-in-javascript

【讨论】:

  • 我认为这不能回答问题。 OP 从未将 dataresponse 视为回调函数。
  • 我认为这个答案有点误导。 then 确实注册了一个回调,稍后在 promise 解析时由底层调度程序调用。响应不会传递给then,而是传递给注册的回调
  • @sinanspd 是的,好的,我试图简化。在实践中,第一个then 的输出会传递给第二个then
  • @ema 是的,它不是那样工作的,但是 OP 也从未假设它,所以你的答案不需要指出 then 函数不接收回调。跨度>
【解决方案2】:

我将尝试以不同的方式解释这一点,使用 async/await 运算符。

相当于你的getCountryData函数如下

const getCountryData = async (country) => {
    console.log("after all async calls"); // explanation for the this line below
    try {
        let response1 = await fetch(`https://restcountries.eu/rest/v2/name/${country}`);

        // first then
        console.log('111111111111111');
        if (!response1.ok)
            throw new Error(`Country not found (${response1.status})`);
        let data1 = await response1.json();

        // second then
        console.log('22222222222');
        const neighbour = data1[0].borders[0];
        let response2;
        if (neighbour)
            response2 = await fetch(`https://restcountries.eu/rest/v2/alpha/${neighbour}`);

        // third then
        console.log('333333333');
        if (!response2.ok)
            throw new Error(`Country not found (${response2.status})`);
        let data2 = await response2.json();

        // fourth then
        console.log('44444444444');
    } catch (err) {
        console.error(`${err} ???`);
    } finally {
        console.log('finalyyyy');
    }
}

首先,您会看到函数最后一行中的console.log 变成了我函数中的第一行。这是因为该行是在异步函数fetch 完成之前执行的。

如您所见,fetch 的最后一个返回变量在第一个 then 中可用,then 的最后一个返回变量在下一个 then 中可用。

  1. 那么对于您的第一个问题,谁调用了下一个then 中的回调函数?显然你可以从你的函数的async/await 等效项中看到,它是按顺序执行的。前一个then 的返回结果将作为下一个then 的参数。因此,在第二个then 中,当您执行return; 时,它不会停止执行第三个then,而是第三个then 将接收undefined 作为response 参数。

  2. 如问题 1 中所述,只要没有未处理的错误或被拒绝的承诺,then 函数就会自动按顺序执行。但是,对于第二个/第三个/第四个then 回调函数从第一个then 访问参数,不可能直接访问,除非您将它与后续then 的每个返回一起传递。这可以通过使用async/await 运算符而不是then 轻松实现。

  3. 当抛出错误时,代码如何知道跳转到 catch 函数?在异步函数中,抛出错误几乎等同于拒绝承诺。这样想,每个then 回调都包装在一个try/catch 块中,该块将调用catch 中的函数。

编辑:如果你做fetch().then().then().catch(),所有的错误或者被拒绝的promise都会在catch函数中处理。 但是如果你做fetch().catch().then().then(),只有来自fetch的错误或者被拒绝的promise会在catch函数中被处理。

【讨论】:

  • 谢谢,但我无法理解回调返回的内容。如果 `let data1 = await response1.json();` 行返回一个 Promise 对象,那么 const neighbour = data1[0].borders[0]; 行如何工作? data 变量来自Promise 类型,而Promise 没有data 属性
  • "第二个 then 回调,不可能直接从第一个 then 访问参数" - 除非你 simply nest them,这正是 await在内部进行
  • "不管你是fetch().then().then().catch() 还是fetch().catch().then().then(),它们的工作原理都是一样的" - 不,绝对不是。
  • @Eitanos30 一个简单的答案是肯定的
猜你喜欢
  • 2018-12-27
  • 2013-10-18
  • 1970-01-01
  • 2019-01-12
  • 1970-01-01
  • 2012-08-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多