js 前端 异步 async await promise resolve reject generator yield setTimeout 请求 原理 面试

本文深入探讨JavaScript中的异步编程,从回调函数的使用讲到Promise的出现,再到Generator和现代的async/await。通过实例解析Promise的工作原理、A+规范以及如何手撕Promise,最后介绍Generator的基本概念和async/await的语法糖特性,帮助开发者更好地理解和运用这些异步编程工具。
摘要由CSDN通过智能技术生成

最近在写【重拾前端】系列,下面有几个快速通道,大家自取

【重识前端】原型/原型链和继承

【重识前端】闭包与模块

【重识前端】全面攻破this

【重识前端】一次搞定JavaScript的执行机制

【重识前端】什么是BFC、IFC、GFC 和 FFC

【重识前端】深入内存世界

【重识前端】暴走的异步编程

前言

老规矩,还是先了解一下什么是异步。异步其实是一个相对比较高级的一个概念。

通常来说,程序都是顺序执行,同一时刻只会发生一件事。如果一个函数依赖于另一个函数的结果,它只能等待那个函数结束才能继续执行,从用户的角度来说,整个程序才算运行完毕.

如果说一个事情需要等待上一件事情做完才能做,但是他们之前又没有强耦合关系。这样就在那里干等就毫无意义。特别是现在计算机普遍都有多核CPU的时代。

举一个形象的🌰。

我们现在要去吃海底捞(海底捞打钱!),我们发现要排队,需要排2个小时左右,这个时候我们要干嘛?难道是干等吗?肯定不是!那样蠢蠢的硬等很呆,我们可能会拿出手机刷刷朋友圈,或者玩一把紧张刺激的王者荣耀又或者去看一场电影等等…在我们做这些事情的时候我们的排队并没有被耽误,甚至可以和服务员说快到了打个电话给我(这个是常见的callback方法,待会讨论)

这样你可以同时完成其他工作,这就是异步编程的出发点。

本文总结了几个异步的方案,理解它们可以让你写出结构更合理、性能更出色、维护更方便的Javascript程序。

异步callback

上来就是一个英俊的例子

var response = fetch('myImage.png');
var blob = response.blob();

没有人可以完美预测到每种网络情况下这个response,是否可以完美获得fetch获得的内容,很有可能会报错。

这个时候我们的callback就✨bu ling bu ling的登场啦。

有些小伙伴可能觉得callback似乎和自己没有太大关系。。。emmmm…

btn.addEventListener('click', () => {
   
  alert('You clicked me!');
});

这样呢?有没有感觉有点儿熟悉了,其他这个就相当于react中的

<div οnclick={() =>{alert('You clicked me!');}}>
	点我  
</div>

是不是很熟悉,这个其实就是最常见的callback(回调函数)。

又或者比较常见的

setTimeout(() => {
   
	alert('You clicked me!');
}2000)

很常见,大致意思就是2秒钟之后会回调一下这个匿名函数,虽然不一定会准确执行,这个涉及到事件循环,感兴趣的可以去找一下我前阵子写的事件循环😊。

发布-订阅模式

发布-订阅模式:又称为观察者模式,它定义对象间的一种一对多的依赖关系,当一个对象的状态改变时,所有依赖于它的对象都将得到通知。

在写React的时候,有一个非常非常非常常见的🌰:

import React, {useState, useEffect} from 'react';
const test = () => {
  const [derrick, setDerrick] = useState(0)
  useEffect(()=> {
    console.log(derrick)
  }, [derrick])
  return(
    <button onClick={() => {setDerrick(derrick+1)}}>点我</button>
  )
}

useEffect订阅了derrick这个变量的变化,然后监听到变化之后就会进行一次log,打印出derrick变量的值。

Promise

回调救星

之前介绍过回调函数这种异步编程。

加入我希望在A执行完之后执行B,然后执行C,然后执行D…最后执行Z

写出来的代码可能是这样的

var sayhello = function (name, callback) {
   
  setTimeout(function () {
   
    console.log(name);
    callback();
  }, 1000);
}
sayhello("first", function () {
   
  sayhello("second", function () {
   
    sayhello("third", function () {
   
      console.log("end");
    });
  });
});

像本例一样嵌套多个方法调用会创建错综复杂的代码,会难以理解与调试。当想要实现更复 杂的功能时,回调函数也会存在问题:若想让两个异步操作并行运行,并且在它们都结束后 提醒你,那该怎么做?若想同时启动两个异步操作,但只采用首个结束的结果,那又该怎么 做?

在这些情况下,你需要追踪多个回调函数并做清理操作, 不过好在promise横空出世拯救了世界。

涉及面试题

promise用过吗?请你介绍一下promise。

其实这里很多同学可能就不知道怎么介绍了,可能只停留在怎么用。我们可以从几个方面来介绍。

  • promise是什么?
  • promise解决了什么问题
  • promise的优缺点
promise是什么?

所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。

promise解决了什么问题?
  1. 解决可读性的问题
  2. 解决信任问题
可读性

可读性,之前的回调地狱promise就可以优雅的解决。(还有更加优雅的,待会会介绍。)

var sayhello = function (name) {
   
  return new Promise(function (resolve, reject) {
   
    setTimeout(function () {
   
      console.log(name);
      resolve();  //在异步操作执行完后执行 resolve() 函数
    }, 10000);
  });
}
sayhello("first").then(function () {
   
  return sayhello("second");  //仍然返回一个 Promise 对象
}).then(function () {
   
  return sayhello("third");
}).then(function () {
   
  console.log('end');
}).catch(function (err) {
   
  console.log(err);
})
信任问题

这个问题可以用promise的几个特点来解释:

(1)对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。

(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。

由于Promise只能被决议一次,且决议之后无法改变,所以,即便是多次回调,也不会影响结果,决议之后的调用都会被忽略。

//信任问题演示

//回调
function method(cb){
   
    setTimeout(function(){
   
        cb && cb();
        //因为某些bug导致某个函数多执行了一次
        cb && cb();
    },1000);
}

//promise
function method2(){
   
    return new Promise(resolve=>{
   
        setTimeout(function(){
   
            resolve();
            //resolve成功调用一次之后,后面的不会再执行
            resolve();
        },1000);        
    })
}

控制反转。体现在对于函数的把控,比如this等。

//回调
function method(cb){
   
    setTimeout(function(){
   
        cb && cb.call({
   a:1,b:2});//执行回调,但是添油加醋
    },1000);
}

//promise
function method2(){
   
    return new Promise(resolve=>{
   
        setTimeout(function(){
   
            resolve();//调用的resolve都是自己写的,改善了控制反转的问题
        },1000);        
    })
}

手撕A+规范的promise

面试题:你知道promise的A+规范吗?(本人在浩鲸科技被问到的,当时答的支支吾吾。。。)

我何止是知道,我甚至可以手写一个符合A+规范的promise!

A+规范快速入口,点我

function MyPromise(executor) {
   
  // 报错this的指向,后面可能会有回调函数,然后会隐式丢失啊之类的。
  let self = this;
  // 当前的状态(fulfilled/rejected/pending),默认值为pending
  self.status = 'pending';
  // 存储成功的值
  self.value = null;
  // 存储失败的值
  self.reason = null;
  function resolve(value) {
   
    // 2.1.1规定只有在pending状态的时候才可以转变
    if(self.status === 'pending') {
   
      // 存下成功的值
      self.value = value
      self.status = 'fulfilled';
    }
  }
  function reject(reason) {
   
    // 2.1.1规定只有在pending状态的时候才可以转变
    if(self.status === 'pending') {
   
      // 存下失败的值
      self.reason = reason
      self.status 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值