深入聊聊async&Promise
正文
最近在学习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>,很奇怪。
我反思可能有两个问题:
- createImg(img)的响应速度太快了,这导致在不同协程间切换的时候,已经fulfilled了;
- 又或者是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的更多相关文章
- async + promise 解决回调地狱
// 解决异步回调地狱的方案: async + promise async function writeFile() { // 打开文件 const fd = await new Promis ...
- react-redux: async promise
1.the simple sample action: 事实上,只是返回个一个至少包含type的对象{ },用于reducer接收. import {RECEIVE_DATA} from " ...
- JS异步事件顺序:setTimeout,async,promise
为什么最近更新那么频繁,还不是因为笔试的时候瞎了? 先说异步事件执行顺序的规则: 1. 定时器异步队列和promise队列不是同一队列,promise优先级高于setTimeout; 2. 创建pro ...
- promise async await使用
1.Promise (名字含义:promise为承诺,表示其他手段无法改变) Promise 对象代表一个异步操作,其不受外界影响,有三种状态: Pending(进行中.未完成的) Resolved( ...
- async/await 执行顺序详解
随着async/await正式纳入ES7标准,越来越多的人开始研究据说是异步编程终级解决方案的 async/await.但是很多人对这个方法中内部怎么执行的还不是很了解,本文是我看了一遍技术博客理解 ...
- 如何避免 async/await 地狱
简评:async/await 写着很爽,不过要注意这些问题. async/await 让我们摆脱了回调地狱,但是这又引入了 async/await 地狱的问题. 什么是 async/await 地狱 ...
- 如何避免 await/async 地狱
原文地址:How to escape async/await hell 译文出自:夜色镇歌的个人博客 async/await 把我们从回调地狱中解救了出来,但是如果滥用就会掉进 async/await ...
- 学JS的心路历程-Promise(三)
今天我们来说then一些特殊情况以及Promise.all()与Promise.race(). 我们都知道函式作为参数传入时,可以参照的方式传入,也能传入时执行拿回传值作使用: function us ...
- 异步编程的类型系统:promise & future & closure & observable----异步编程类型的结构和操作
异步编程类型的结构和操作. 上下文维护. A promise represents the eventual result of an asynchronous operation. The prim ...
- 前端er,你真的会用 async 吗?
async 异步函数 不完全使用攻略 前言 现在已经到 8012 年的尾声了,前端各方面的技术发展也层出不穷,VueConf TO 2018 大会 也发布了 Vue 3.0的计划.而在我们(我)的日常 ...
随机推荐
- 当git仓库里面已经有上传好的框架时,二次上传到仓库需要的指令
初始化仓库 1 git init git add . "提交信息"里面换成自己的需要 如"first commit" git commit -m "提 ...
- Trace32 simulator调试以及简单实用命令介绍
目录 Trace32 Simulator debug Trace32工具配置 Trace32命令简介 memory class 常见命令索引 v.v使用实例 不同CPU运行信息查看 Trace32 S ...
- 【首场重磅亮相】KaiwuDB 1.0 时序数据库线上发布会明日开启!邀您共同见证
首场重磅亮相 KaiwuDB 是浪潮集团控股的数据库企业,以多模数据库为核心,面向工业物联网.数字能源.交通车联网.智慧城市.数字政务等多种场景,提供领先创新的数据服务软件. 新生代数据库,扬帆起航正 ...
- style 标签写在body 前后的区别?
知识储备:了解浏览器渲染页面的流程 a)首先 , 解析(parse)html 标签 , 获取DOM 树 b)解析html 的同时 , 解析css , 获得样式规则 (style rules) CSS ...
- 小程序的image组件
mode属性:用来制定图片的裁剪和缩放模式:常用属性如下:
- 12. $nextTick 的作用
使用场景:我们改变 dom 结构所依赖的数据的时候,不能直接操作 dom ,因为 dom 还没有更新完成 : 作用 : nextTick 用来感知 dom 的更新完成,类似于 updated 函数 : ...
- KubeSphere 社区双周报 | Java functions framework 支持 SkyWalking | 2023.8.4-8.17
KubeSphere 社区双周报主要整理展示新增的贡献者名单和证书.新增的讲师证书以及两周内提交过 commit 的贡献者,并对近期重要的 PR 进行解析,同时还包含了线上/线下活动和布道推广等一系列 ...
- Tortoise SVN 安装汉化教程(乌龟SVN)
1.首先下载 去官网下载 如果下载比较慢的,链接自取 https://pan.quark.cn/s/cb6f2eee3f90 2. 安装Tortoise SVN 无脑next到完成 最后到桌面右键 你 ...
- Java中“=”克隆理解
在Java中,对于基本类型可以使用"="来进行克隆,此时两个变量除了相等是没有任何关系的. 而对于引用类型却不能简单地使用"="进行克隆,这与java的内存空间 ...
- CF980-Div2-D
CF980-Div2-D 题意 从 \(1\) 开始决策,若选当前数,则累计贡献 \(a[i]\) 并跳到 \(j\) 位置,\(j\) 是 \(\lt i\) 且没有决策过(包括选了和没选)的最大位 ...