正文

最近在学习JavaScript里的async、await异步,对于其中的Promise状态以及背后的Js引擎实际运行状态不大理解且很感兴趣,因此花了一点时间仔细研究了一下。

从Example说起

const createImg = function (path) {
return new Promise((resolve, reject) => {
const img = document.createElement('img');
img.src = path;
img.classList.add('images');
img.addEventListener('load', () => {
imgContainer.append(img);
resolve(img);
}); img.addEventListener('error', () => {
reject(new Error('image has not found!'));
});
});
}; const loadImg = async function(imgPath){
const imgs = imgPath.map(async img => await renderImg(img));
console.log(imgs); // Question
} loadImg(['./img/img-1.jpg', './img/img-2.jpg', './img/img-3.jpg'])

其中imgPath是图片地址数组,loadImg是遍历渲染图片的异步函数,renderImg是在当前页面插入并渲染图片的一个异步函数。我的疑问从Question Line开始,为什么控制台打印出的是Promise < fulfilled: undefined>

async机制

我开始从async在Js引擎中的执行逻辑学起:async会开辟一个单独的协程,并且当执行到其中await行(前提是返回Promise)时,将await后表达式放入Web APIs后台运行。

遵循这个执行逻辑分析,loadImg首先开辟一个loadImg协程,并且在执行到imgPath.map(async img...)行时,单独为几个img异步函数分别开辟了协程,为方便后文称img1、img2、img3协程

图中箭头表示当前CALLSTACK中执行的是哪个协程的代码内容,由于await createImg(img)中createImg是一个返回Promise的异步函数,因此会把createImg放入Web APIs中,并立即返回一个Promise< pending >。

[!NOTE]

这里值得注意的是,createImg返回的Promise< pending>是返回在Img协程内部的,也就是说如果有一个变量可以接收,例如 const tmp = await createImg(img),则这个Promise是赋值给tmp的;但是async调用之后会立即返回一个Promise< pending >对象(后文详细描述为什么打印出来的是Promise< fulfilled: undefined >,与之不同)。

最终可以看见在LoadAll协程处返回了3个Promise< pending >对象,但是console.log出来的还是Promise< fulfilled: undefined>,很奇怪。

我反思可能有两个问题:

  1. createImg(img)的响应速度太快了,这导致在不同协程间切换的时候,已经fulfilled了;
  2. 又或者是async返回的永远都是Promise< fulfiiled>?
const loadAll = async function (ImgArr) {
const imgs = ImgArr.map(async Img => {
console.log('start rendering');
await new Promise(resolve => setTimeout(resolve, 2000));
await createImg(Img);
console.log('end rendering');
return Img;
});
console.log(imgs);
};

这次我在createImg前加了2s的阻塞,这样就能知道到底是谁的问题了。

结果还是Promise< fulfilled: [[value]]>? 实话说真有点懵了。

理性分析来说,Promise是即刻返回的,所以不存在看后续代码中是否能跑通,是否有bug等,所以理论上来说可以直接排除问题2,因为通常来说pending可以到fulfilled/reject两个状态

在我多次尝试之后,发现...

console控制台实时渲染

直接上结论:

Promise返回的就是Promise< pending>,也只能返回这个对象,而其中的fulfilled /reject状态是当async中的异步代码后台跑完后返回给console,并由console去动态渲染替换的

证据:

在阻塞的2s期间内点击控制台的Promise对象数组,可以看到在async整体代码没跑完的时候,每个Promise都是pending的状态,等2s阻塞期过后,由于createImg的速度很快,一下就可以由console完成由pending ->fulfilled状态的动态渲染替换。

问题

None

总结

至此破案,也让我更进一步的理解async、Promise、await的机制。

深入聊聊async&Promise的更多相关文章

  1. async + promise 解决回调地狱

    // 解决异步回调地狱的方案: async + promise async function writeFile() {   // 打开文件   const fd = await new Promis ...

  2. react-redux: async promise

    1.the simple sample action: 事实上,只是返回个一个至少包含type的对象{ },用于reducer接收. import {RECEIVE_DATA} from " ...

  3. JS异步事件顺序:setTimeout,async,promise

    为什么最近更新那么频繁,还不是因为笔试的时候瞎了? 先说异步事件执行顺序的规则: 1. 定时器异步队列和promise队列不是同一队列,promise优先级高于setTimeout; 2. 创建pro ...

  4. promise async await使用

    1.Promise (名字含义:promise为承诺,表示其他手段无法改变) Promise 对象代表一个异步操作,其不受外界影响,有三种状态: Pending(进行中.未完成的) Resolved( ...

  5. async/await 执行顺序详解

    随着async/await正式纳入ES7标准,越来越多的人开始研究据说是异步编程终级解决方案的 async/await.但是很多人对这个方法中内部怎么执行的还不是很了解,本文是我看了一遍技术博客理解 ...

  6. 如何避免 async/await 地狱

    简评:async/await 写着很爽,不过要注意这些问题. async/await 让我们摆脱了回调地狱,但是这又引入了 async/await 地狱的问题. 什么是 async/await 地狱 ...

  7. 如何避免 await/async 地狱

    原文地址:How to escape async/await hell 译文出自:夜色镇歌的个人博客 async/await 把我们从回调地狱中解救了出来,但是如果滥用就会掉进 async/await ...

  8. 学JS的心路历程-Promise(三)

    今天我们来说then一些特殊情况以及Promise.all()与Promise.race(). 我们都知道函式作为参数传入时,可以参照的方式传入,也能传入时执行拿回传值作使用: function us ...

  9. 异步编程的类型系统:promise & future & closure & observable----异步编程类型的结构和操作

    异步编程类型的结构和操作. 上下文维护. A promise represents the eventual result of an asynchronous operation. The prim ...

  10. 前端er,你真的会用 async 吗?

    async 异步函数 不完全使用攻略 前言 现在已经到 8012 年的尾声了,前端各方面的技术发展也层出不穷,VueConf TO 2018 大会 也发布了 Vue 3.0的计划.而在我们(我)的日常 ...

随机推荐

  1. 系统编程-进程-ps命令、进程调度、优先级翻转、进程状态

    1.    ps详解 ps  : 只列出当前用户的进程 ps -ef : e表示有效, f 表示全面, 所以是列出后台的所有有效进程. ps -ef | more :   列出后台所有的有效进程,并且 ...

  2. ASP.NET Core OData 9的发布,放弃 .NET Framework

    Microsoft 于 2024 年 8 月 30 日宣布推出 ASP.NET Core OData 9 包. 这个新包将ASP.NET Core与.NET 8 OData库保持一致,改变了OData ...

  3. 活动预告 | 中国数据库联盟(ACDU)中国行定档深圳,一起揭秘数据库前沿技术

    在当今数字化时代,数据库是各行各业中最核心的信息管理系统之一.随着技术的飞速发展,数据库领域也不断涌现出新的前沿技术和创新应用.数据库运维和开发人员需要紧跟前沿技术,才能保持竞争力,并实现更高效.更智 ...

  4. 托管服务简介IHostedService接口 继承 BackgroundSerice接口

    1. 场景:代码运行在后台,比如服务器启动的时候在后台预先加载数据到缓存,每天凌晨3 点把数据到处到数据库备份,每隔5秒在两张表之间同步一次数据 : 2. 托管服务实现IHoutedService接口 ...

  5. 云原生周刊:Kubernetes 1.29 中的删除、弃用和主要更改 | 2023.11.27

    开源项目推荐 Orphaned ConfigMaps 该版本库包含一个脚本,用于识别 Kubernetes 命名空间中的孤立的配置映射.孤立的配置映射是指那些未被命名空间中的任何活动 Pod 或容器引 ...

  6. 160. 相交链表 Golang实现

    题目描述: 给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点.如果两个链表不存在相交节点,返回 null . 注意这里的相交节点表示的是值和物理位置都相同的 ...

  7. element-ui带输入建议的input框踩坑

    踩坑问题描述: 问题一: 获取到后端返回的数组,并将数组传入作为 results 传入 callback 后,焦点放在 输入框 上的时候,并未出现任何内容,只出现了一个不完整的空白框. 问题解决方案: ...

  8. vivo 轩辕文件系统:AI 计算平台存储性能优化实践

    在早期阶段,vivo AI 计算平台使用 GlusterFS 作为底层存储基座.随着数据规模的扩大和多种业务场景的接入,开始出现性能.维护等问题.为此,vivo 转而采用了自研的轩辕文件系统,该系统是 ...

  9. JavaScript String 对象-常用知识点

    JavaScript String 对象-常用知识点 对象用于处理文本(字符串). 对象创建方法: new String(). String 对象属性 属性 描述 constructor 对创建该对象 ...

  10. CF939 D

    CF939 D 让你把区间分成 \(k\) 段, 段内用 \(xor\) 连接, 段之间用 \(or\) 连接,问你在结果不大于 \(x\) 的前提下, \(k\) 的最大值 \(1 \leq n \ ...