当前位置: 首页 > news >正文

半理解系列--Promise的进化史

半理解系列--Promise的进化史

学过js的都知道,程序有同步编程和异步编程之分,同步就好比流水线,一步一个脚印,做完上个任务才做下一个,异步编程好比客服,客服接了一个电话,收到了一个任务,然后把任务交给另外的人来处理,同时,继续接听下一个电话,等到另外的人处理完任务了,再通知客服,客服再反馈给第一个打电话的人。异步编程一般用来调取接口拉数据。

通过我描述的篇幅,就知道异步编程比同步编程麻烦许多。远古时期,异步编程是通过回调函数来解决的。但是回调函数会有回调地狱的问题,回调的多了,维护人员看起来头都大了,好比:taskC需要等待taskB做完(taskC才执行),taskB又需要等待taskA做完(taskB才执行)

function taskA (cb) {
  //..do task A
  cb()
}
function taskB(cb){
  //..do task B
  cb()
}
function taskC(cb){
  //..do task C
  cb()
}
taskA(function(){
  taskB(function(){
    taskC()
  })
})
...以此类推,不断循环嵌套,最终陷入地狱

而Promise就把这一系列的回调,通过链式调用的方式连接起来,看起来清爽多了。同样是上面的代码,Promise可以这么写(伪代码)

new Promise().then(taskA).then(taskB).then(taskC)

promise的使用

const promise = new Promise((resolve,reject)=>{
  if (/*do something done*/){
    resolve() // 可在此传参数
  } else {
    // do something fail
    reject() // 可在此传参数
  }
})
promise.then(()=>{
  //do something
}).catch(e => { throw e})

上面的resolve,可以当作task函数的cb回调函数,当resolve()执行的时候,then方法中的回调会被执行,如果是reject执行,错误会被catch捕捉。

Promise的静态方法

上面说的thencatch都是Promise的原型方法,即Promise.prototype.then/catch
Promise本身有两个静态方法,其作用类似 new Promise()

Promise.resolve()

const promise1 = Promise.resolve()

等价于

const promise2 = new Promise((reslove)=>{
  reslove()
})

使用该方法调用then方法
Promise.reject()

const promise1 = Promise.reject()

等价于

const promise2 = new Promise((resolve,reject)=>{
  reject()
})

使用该方法会被catch捕捉

Promise的链式调用

Promise的实例对象的then方法是可以重复调用的,then方法返回的是一个promise实例对象,所以可以重复调用then方法,并且(敲黑板),上一个then方法的返回值,会作为下一个then方法的参数传递

举个栗子:

const promise = Promise.resolve('start')

promise.then((params)=>{
  console.log(params) // start
  return 'aa'
}).then((params) => {
  console.log(params) // aa
  return 'bb'
}).then((params)=>{
  console.log(params) // bb
  return 'cc'
})

// 最后会返回一个状态是resolve(cc)的promise对象:Promise {<resolved>: "cc"}

深入一下(又不会怀孕)

function badAsyncCall() {
  var promise = Promise.resolve();
  promise.then(function() {
      // 任意处理
      return 'newVar';
  });
  return promise;
}
// 修改一下
function goodAsyncCall() {
  var promise = Promise.resolve();
  return promise.then(function() {
      // 任意处理
      return 'newWar';
  });
}

以上两个写法是不是很相似,唯一不同的就是return的处理。但调用,badAsynccall会出错,而anAsyncCall能正确执行,比如:

badAsyncCall().then(params => { console.log('bad--',params)}) // bad-- undefined
goodAsyncCall().then(params => { console.log('good--',params)}) // good-- newWar

分析:第一种,错误写法,首先在 promise.then 中产生的异常不会被外部捕获,此外,也不能得到 then 的返回值,即使其有返回值。
原因:由于每次 promise.then 调用都会返回一个新创建的promise对象,第一种返回的promise,相当于没有调用过函数内部的then方法,是一个全新的promise实例对象
结论: 统一使用promise链式调用,如:promise.then(taskA).then(taskB)

### async&await和promise的前世缘缘

promise说白了还是用回调的方式来解决异步问题,跟真正同步还是有差距的。
异步编程的最高境界,就是根本不用关心它是不是异步!(来之ruanyifeng老师的话)

所以,async&await方案出现了

用法:

function readFile(fileName) {
  return new Promise((resolve,reject)=>{
    fs.readFile(fileName, function(error, data) {
      if (error) return reject(error);
      resolve(data); // 向thenc传送异步读取文件的数据
    });
  })
}

// 调用

readFile(fileName).then(function(data){
  console.log('prmoise read files data --', data)
})


// 等价于

async function asyncFn(fileName){
  const data = await readFile(fileName)
  console.log('await data --', data)
  return data
}

asyncFn(fileName) 

写法是不是简洁了许多!
其实async就是一个Promise的语法糖,它的返回值是一个promise对象,因此可以用then方法做链式调用(但参数就是async函数中的返回值,如上文的data!!)

async函数中还可以不使用promise,比如:

async function asyncFn(){
  const data = await setTimeout(function(){
    console.log('setTimeout') 
    return 'data'
  },1000)
  console.log('data',data) // Timeout {} 对象
}
console.log('async',asyncFn()) // Promise { <pending> }

但这两者,其实经常混用,常见的就是readFile函数的做法啦

看懂以上的,才大家出一道题看看能不能懂;

async function asynFn(){ 
  await Promise.resolve('aaa')
  const data = {
    b:'bb',
    c:function(){ return this.b }
  }
  return data  //return 作为参数传递给then then的chain链也是通过return参数来不断传递给后面的then
}
var cball = asynFn()
cball.then(function(data){
  console.log('data:',data)
})

还有一种异步编程的语法糖: * & yield
跟async基本一样,不在本文讨论的重点。有兴趣自行google啦

参考资料:
async 函数的含义和用法
Promise 对象

相关文章:

  • 游戏引擎大全
  • 【Lv1-Lesson008】A Guide to Birthdays
  • ARM9 S3C2440 定时器中断
  • 可复制的领导力(来自樊登读书会)
  • C++中的const成员函数(函数声明后加const,或称常量成员函数)用法详解
  • 孩子做错事不认错怎么办
  • iOS app被审核拒绝后如何打开解决方案中心的记录
  • Tiny4412裸机程序,按键检测
  • 带给你设计灵感的30个超棒的暗色系网站设计
  • Linux内核中锁机制之内存屏障、读写自旋锁及顺序锁
  • Web Service概述
  • ORACLE PL/SQL编程之 触发器
  • Android开发历程_9(Frame Animation的使用)
  • HTML5与XML的区别
  • 《JavaScript高级程序设计》笔记
  • [微信小程序] 使用ES6特性Class后出现编译异常
  • eclipse的离线汉化
  • extjs4学习之配置
  • flask接收请求并推入栈
  • Flex布局到底解决了什么问题
  • Java 11 发布计划来了,已确定 3个 新特性!!
  • JavaScript HTML DOM
  • JavaScript对象详解
  • JavaScript新鲜事·第5期
  • mongodb--安装和初步使用教程
  • PHP 7 修改了什么呢 -- 2
  • Python利用正则抓取网页内容保存到本地
  • QQ浏览器x5内核的兼容性问题
  • spring学习第二天
  • 闭包,sync使用细节
  • 七牛云假注销小指南
  • 前端每日实战:70# 视频演示如何用纯 CSS 创作一只徘徊的果冻怪兽
  • 时间复杂度与空间复杂度分析
  • 说说动画卡顿的解决方案
  • 怎样选择前端框架
  • !! 2.对十份论文和报告中的关于OpenCV和Android NDK开发的总结
  • # Pytorch 中可以直接调用的Loss Functions总结:
  • #鸿蒙生态创新中心#揭幕仪式在深圳湾科技生态园举行
  • (1) caustics\
  • (LeetCode C++)盛最多水的容器
  • (ZT)一个美国文科博士的YardLife
  • (多级缓存)多级缓存
  • (免费领源码)python#django#mysql校园校园宿舍管理系统84831-计算机毕业设计项目选题推荐
  • (三维重建学习)已有位姿放入colmap和3D Gaussian Splatting训练
  • (算法)前K大的和
  • (原創) 如何優化ThinkPad X61開機速度? (NB) (ThinkPad) (X61) (OS) (Windows)
  • (转)Linux NTP配置详解 (Network Time Protocol)
  • (转)甲方乙方——赵民谈找工作
  • (转载)VS2010/MFC编程入门之三十四(菜单:VS2010菜单资源详解)
  • .\OBJ\test1.axf: Error: L6230W: Ignoring --entry command. Cannot find argumen 'Reset_Handler'
  • .NET core 自定义过滤器 Filter 实现webapi RestFul 统一接口数据返回格式
  • .NET Core实战项目之CMS 第十二章 开发篇-Dapper封装CURD及仓储代码生成器实现
  • .Net 应用中使用dot trace进行性能诊断
  • .net和jar包windows服务部署
  • .sh