Promise从入门到手写 | [Promise系列一](一)

简介: Promise从入门到手写 | [Promise系列一](一)

写作背景


其实我最开始想写的并不是Promise这篇文章,而是想总结一些axios相关的知识,但是写文章必须有所依据,想要去讲好axios,那么对他的前置知识一定要了解,所以我想不妨把这做成一个系列,于是就有了从PromiseAjax开始,再去讲Axios的打算。其实相信大家做前端开发的一定对Promise不陌生了,所以大家伙也不妨跟着我对Promise这个基础知识进行回顾。 本文将包括:


  • Promise介绍
  • Promise特点
  • Promise使用
  • 根据前文的使用方式和特性,手写Promise


话不多说,我们直接开始!


Promise介绍


Promise起源与用途


  • Promise最早在社区提出和实现,在ES6写入了语言标准。


tip: ES6即ECMA-262第六版,这一版包含了这个规范有史以来最重要的一批增强特性,Javascript是ECMA-262规范的实现


  • Promise是异步编程的解决方案,比传统的回调函数解决方式更加合理更加强大更加优雅。
  • 语法上: 使用Promise构造函数对异步操作进行封装以生成Promise实例
  • 功能上: promise对象用来封装一个异步操作并提供统一的API,使得各种异步操作都可以用同样的方式进行处理


常见的异步编程场景


  • fs文件操作


require('fs').readFile('./index.html',(err,data)=>{
    // 回调函数
})


  • Ajax操作


$.get(
/api/getUser',(data)=>{
    //handleData();
})


  • 定时器


setTimeout(() => {
    console.log('timeout');
},1000);


为什么使用Promise


  • 支持链式调用,将异步操作以同步操作的流程表达出来,可以解决回调地狱问题

什么是回调地狱?


回调函数嵌套调用,外部回调函数异步执行结果是嵌套的回调的执行条件


// 回调地狱典型场景
asyncFunc1(opt,(...args1) => {
    asyncFunc2(opt,(...args2) => {
        asyncFunc3(opt,(...args3) => {
            asyncFunc4(opt,(...args4) => {
                //TODO: some opt
            })
        })
    })
})


回调地狱的缺点:


不便于阅读,不便于异常处理,不利于身心愉快

  • 指定回调函数的方式更加灵活

传统方式:  必须在启动异步任务之前指定

Promise:  可以随时监听异步任务的状态,随时指定回调函数,一个或者多个。

  • Promise提供统一的api,使得控制异步操作更加容易。

提供了哪些api在后续的使用中会详细阐述


Promise特点


Promise的特性


  • 对象状态不受外界影响


Promise对象代表一个异步操作,有三种状态:pending进行中,fulfilled成功,rejected失败 只有异步操作结束才能改变状态,其他任何操作都不能改变。状态存储在Promise对象的[[PromiseState]]属性中。


// Promise的两个属性:
let promiseA = new Promise((resolve,reject)=>{resolve();})
// 状态对应promiseA的[[PromiseState]]字段。
let promiseB = new Promise((resolve,reject)=>{resolve(1111);})
// [[PromiseResult]]的值为 1111,这个字段用于存储 resolve(val)或者reject(val)的参数[val]


tips: Promise本意是承诺,也是因为此原因,想一想这是多么浪漫的名字。


  • 一旦状态改变,就不会再发生变化


Promise 对象的状态改变,只有两种可能:从 Pending 变为 Resolved 和从 Pending 变为 Rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。就算改变已经发生了,你再对 Promise 对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。


Promise的缺点


  • 一旦新建,立即执行,无法中途取消
  • Promise内部抛出的错误,无法反映到外部
  • pending状态时无法知道进展到哪一个阶段


Promise使用


Promise实例创建


const promise = new Promise((resolve, reject) => {
  if (/* 异步操作成功 */){
    resolve(value);
  } else {
    reject(error);
  }
});


Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolvereject。它们是两个函数,由 JavaScript 引擎提供,不用自己部署。


  • resolve函数:将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 fulfilled),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
  • reject函数:将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。


Promise.prototype.then


Promise实例生成以后,可以用then方法分别指定fulfilled状态和rejected状态的回调函数


最终会返回一个新的Promise对象


tip: then中的回调函数是可选的,不一定要提供


promise.then(function(value) {
  // fulfilled状态的处理
}, function(error) {
  // rejected状态的处理
});


Promise.prototype.catch


catch用于处理状态为rejected的回调函数

最终会返回一个新的Promise对象


promise.catch((err)=>{
    handleReject();
    //
})


Promise.resolve


返回成功或者失败的Promise对象


let promiseA = Promise.resolve(1);
// 如果传入的参数为非Promise类型的对象,则返回的结果为成功的Promise对象
let PromiseB = Promise.resolve(new Promise((resolve,reject)=>{
    reject('err');
})
// 如果传入的参数为Promise对象,则参数Promise返回的结果就是 Promise.resolve返回的结果
// 比如这时return 一个[[PromiseResult]]的值为err的Promise对象


Promise.reject


返回一个失败的Promise对象


let PromiseA = Promise.reject(new Promise((resolve,reject)=>{
    resolve('err');
})
// 无论传入是啥,就返回一个失败的Promise对象,[[PromiseResult]]的值为 Promise.reject的参数


Promise.all


接收的参数是由n个Promise对象的数组。


tips: 返回结果是新的promise,只有所有的Promise对象都成功才成功,只要有一个失败了就直接失败


let promise1 = Promise.resolve(1);
let promise2 = Promise.resolve(2);
let promise3 = Promise.reject(3);
const res = Promise.all([promise1,promise2,promise3]);
//此时输出为 [[PromiseState]]是rejected,[[PromiseResult]]是3的Promise
const res = Promise.all([promise1,promise2]);
//此时输出为 [[PromiseState]]是fulfilled,[[PromiseResult]]是[1,2]的Promise


Promise.race


接收的参数是由n个Promise对象的数组。


tips: 返回结果是新的promise,第一个完成的promise的结果状态就是最终结果的状态。


let promise1 = Promise.resolve(1);
let promise2 = Promise.resolve(2);
let promise3 = Promise.reject(3);
const res = Promise.race([promise1,promise2,promise3]);
//此时输出为 [[PromiseState]]是fulfilled,[[PromiseResult]]是1的Promise


常见问题


如何改变Promise对象的状态


  • resolve()  // pending => fulfilled
  • reject()   // pending => rejected
  • 抛出错误 throw 'err' // pending => rejected


promise.then() 的返回结果


  • 如果抛出异常,则返回rejected的Promise对象
  • 如果返回的是非promise类型的任意值,则返回状态为resolved的Promise对象
  • 如果返回的是一个新的promise,则该promise的结果会成为新的promise结果


promise为什么可以链式调用


因为then,catch,all,race等等所有的promise的api的返回值是新的promise对象。

所以可以继续打点调用promise的方法,以此种方式将任务串联起来


promise的异常穿透


  • 当使用promise的then进行链式调用时,可以在最后指定失败的回调
  • 前面的任何错误都会在最后传到失败的回调中去处理


let p1 = Promise.resolve(1);
p1.then((value)=>{
    console.log(11);
}).then((value)=>{
    throw 'err';
}).then((value)=>{
    console.log(22);
}).catch(err=>{
    console.log(err);
})
//输出: 11  err


中断promise链


let p1 = Promise.resolve(1);
p1.then((value)=>{
    console.log(11);
}).then((value)=>{
    console.log(22);
}).then((value)=>{
    console.log(33);
}).catch(err=>{
    console.log(err);
})
//输出:11 22 33


那我们怎么去中断这个回调函数的联调呢


let p1 = Promise.resolve(1);
p1.then((value)=>{
    console.log(11);
}).then((value)=>{
    console.log(22);
    return new Promise(()=>{});
}).then((value)=>{
    console.log(33);
}).catch(err=>{
    console.log(err);
})
//输出:11 22


答案就是返回一个状态为pendingpromise对象

相关文章
|
前端开发 JavaScript
Promise入门/面试必看
Promise入门/面试必看
139 0
|
前端开发 JavaScript API
|
前端开发
Promise对象简单入门
Promise对象简单入门
165 0
Promise对象简单入门
|
前端开发 API 容器
ES6入门之Promise
ES6入门之Promise
|
存储 前端开发
Promise从入门到手写 | [Promise系列一](二)
Promise从入门到手写 | [Promise系列一](二)
202 0
|
3月前
|
前端开发 JavaScript API
一文吃透 Promise 与 async/await,异步编程也能如此简单!建议收藏!
在前端开发中,异步编程至关重要。本文详解了同步与异步的区别,通过生活化例子帮助理解。深入讲解了 Promise 的概念、状态及链式调用,并引入 async/await 这一语法糖,使异步代码更清晰易读。还介绍了多个异步任务的组合处理方式,如 Promise.all 与 Promise.race。掌握这些内容,将大幅提升你的异步编程能力,写出更优雅、易维护的代码,助力开发与面试!
226 0
一文吃透 Promise 与 async/await,异步编程也能如此简单!建议收藏!
|
3月前
|
前端开发 JavaScript API
JavaScript异步编程:从Promise到async/await
JavaScript异步编程:从Promise到async/await
449 204
|
前端开发 JavaScript 开发者
Async 和 Await 是基于 Promise 实现
【10月更文挑战第30天】Async和Await是基于Promise实现的语法糖,它们通过简洁的语法形式,借助Promise的异步处理机制,为JavaScript开发者提供了一种更优雅、更易于理解和维护的异步编程方式。
251 1
|
9月前
|
前端开发
使用 async/await 结合 try/catch 处理 Promise.reject()抛出的错误时,有什么需要注意的地方?
使用 async/await 结合 try/catch 处理 Promise.reject()抛出的错误时,有什么需要注意的地方?
395 57