正文

最近在学习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协程

image.png

图中箭头表示
当前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的阻塞,这样就能知道到底是谁的问题了。

image.png

结果还是Promise< fulfilled: [[value]]>? 实话说真有点懵了。
理性分析来说,
Promise是即刻返回的
,所以不存在看后续代码中是否能跑通,是否有bug等,所以理论上来说可以直接
排除问题2
,因为通常来说
pending可以到fulfilled/reject两个状态

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

console控制台实时渲染

直接上结论:

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

证据:

image.png

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

问题

None

总结

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

标签: none

添加新评论