前言

  promise 是前端开发人员必须掌握的知识点,本文来总结一下相关学习笔记。

正文

  1、什么是prommise,promise 解决了什么问题

  a、promise 是什么

  Promise 是承诺的意思,承诺它过一段时间会给你一个结果。Promise 是一种解决异步编程的方案,相比回调函数和事件更合理和更强大。从语法上讲,promise 是一个对象,从它可以获取异步操作的消息;

  promise 有三种状态:pending 初始状态也叫等待状态,fulfiled成功状态,rejected 失败状态;状态一旦改变,就不会再变。创造 promise实例后,它会立即执行。需要注意,promise 的状态是不可逆的,一旦状态由 pending 变为 fulfiled 或者reject 状态,意味着已经产生了结果,同样,转为成功状态会有成功的结果,转为失败状态会返回失败的原因。

  promise 作为构造函数,接收两个参数,分别是成功和失败的回调函数。

  b、promise 解决了什么问题

  我们先来看如下代码,并不陌生

  setTimeout(function () {
  console.log("开始执行");
  }, 3000);

  上面的代码,粗略的可以认为在 3 秒后,程序输出开始执行,但是如果业务比较复杂,我们想在3秒后输出开始执行,再隔 3 秒打印一次第二次执行呢?接着在隔 3 秒打印第三次执行,代码会这样写:

  setTimeout(function () {
   console.log("开始执行");
   setTimeout(function () {
  console.log("第二次执行");
   setTimeout(function () {
   console.log("第三次执行");
   }, 3000);
   }, 3000);
  }, 3000);

  再看上面的代码,如果后面的需求再次优化,需要类似的打印第 4,5,6 次呢?我们的代码还是这样一层层嵌套起来吗? 这样多层函数之间互相嵌套,就产生了回调地狱的问题,这样写代码有个很大的缺点:(1)代码耦合行太强,牵一发而动全身,可维护性很差,同样,大量冗余的代码互相嵌套,可读性很差。因此,为了解决回调地狱的问题,ES6 提出了 Promise。通过 promise 将上面的代码改装一下将显的代码优雅很多:

    function sleep(second) {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(), second * 1000);
});
}
sleep(3)
.then(() => {
console.log("开始执行");
return sleep(3);
})
.then(() => {
console.log("第二次执行");
return sleep(3);
})
.then(() => {
console.log("第三次执行");
});

  2、ES6 中 promise 的使用

  1)then 链式调用

  从表面上看,Promise只是能够简化层层回调的写法,而实质上,Promise 的精髓是“状态”,用维护状态、传递状态的方式来使得回调函数能够及时调用,它比传递 callback 函数要简单、灵活的多。所以使用 Promise 的正确场景是这样的:

    p.then((data) => {
console.log(data);
})
.then((data) => {
console.log(data);
})
.then((data) => {
console.log(data);
});

  then 是实例状态发生改变时的回调函数,第一个参数是 resolved 状态的回调函数,第二个参数是 rejected 状态的回调函数,then方法返回的是一个新的Promise实例,也就是promise能链式书写的原因。默认常写第一个参数即可,reject 状态的回调可以通过 catch 来捕获异常。

  2)catch 方法用来指定 promise 实例状态变为 rejected 的捕获

  catch 捕获reject状态的回调,相当于 then中的第二个参数,一般写法如下:

    p.then((data) => {
console.log("resolved", data);
}).catch((err) => {
console.log("rejected", err);
});

  也就是说进到catch方法里面去了,而且把错误原因传到了reason参数中。即便是有错误的代码也不会报错了,这与我们的try/catch语句有相同的功能。

  3)all 方法将多个 promise 实例包装成一个新的 promise 实例(谁跑的慢,以谁为准执行回调)

  Promise.all 方法接收一个数组(可迭代对象)作为参数,并且数组中的每个元素都是 Promise 实例,最终返回结果也为一个 Promise  对象,例如:

const p = Promise.all([p1, p2, p3]),实例p的状态由p1、p2、p3决定,分为两种:

  只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数;

  只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数;

  常见的写法如下:

    let Promise1 = new Promise(function (resolve, reject) {});
let Promise2 = new Promise(function (resolve, reject) {});
let Promise3 = new Promise(function (resolve, reject) {}); let p = Promise.all([Promise1, Promise2, Promise3]); p.then(
(res) => {
// 三者都成功则成功,成功后处理
},
(err) => {
// 三者只要有失败的就返回失败,失败后处理
}
);

  有了all 方法,我们就可以并行执行多个异步操作,并且在一个回调中处理所有的返回数据,比如开发中打开网页时,预先加载需要用到的各种资源如图片、flash 以及各种静态文件,所有的都加载完后,我们再进行页面的初始化。

  (4)race 方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例(谁跑的快,以谁为准执行回调)

  const p = Promise.race([p1, p2, p3]);只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变,率先改变的 Promise 实例的返回值则传递给p的回调函数。

  使用场景:比如,我们在页面加载的时候,需要请求后端获取某个图片的URL,这里我们可以设置请求的超时时间,当在设定的时间内后端接口没有返回时,页面给出请求超时提示。代码如下:

    //请求某个图片资源
function requestImg() {
var p = new Promise((resolve, reject) => {
var img = new Image();
img.onload = function () {
resolve(img);
}
img.src = '图片的路径';
});
return p;
}
//延时函数,用于给请求计时
function timeout() {
var p = new Promise((resolve, reject) => {
setTimeout(() => {
reject('图片请求超时');
}, 5000);
});
return p;
}
Promise.race([requestImg(), timeout()]).then((data) => {
console.log(data);
}).catch((err) => {
console.log(err);
});

  3、promise 的缺点

  1)无法取消 Promise,一旦新建它就会立即执行,无法中途取消  

  2)如果不设置回调函数,Promise 内部抛出的错误,不会反映到外部

  3)当处于 pending(等待)状态时,无法得知目前进展到哪一个阶段,是刚刚开始还是即将完成

  4、手动实现 Promise

        function resolvePromise(promise2, x, resolve, reject) {
//判断x是不是promise
//规范中规定:我们允许别人乱写,这个代码可以实现我们的promise和别人的promise 进行交互
if (promise2 === x) {//不能自己等待自己完成
return reject(new TypeError('循环引用'));
};
// x是除了null以外的对象或者函数
if (x != null && (typeof x === 'object' || typeof x === 'function')) {
let called;//防止成功后调用失败
try {//防止取then是出现异常 object.defineProperty
let then = x.then;//取x的then方法 {then:{}}
if (typeof then === 'function') {//如果then是函数就认为他是promise
//call第一个参数是this,后面的是成功的回调和失败的回调
then.call(x, y => {//如果Y是promise就继续递归promise
if (called) return;
called = true;
resolvePromise(promise2, y, resolve, reject)
}, r => { //只要失败了就失败了
if (called) return;
called = true;
reject(r);
});
} else {//then是一个普通对象,就直接成功即可
resolve(x);
}
} catch (e) {
if (called) return;
called = true;
reject(e)
}
} else {//x = 123 x就是一个普通值 作为下个then成功的参数
resolve(x)
} } class Promise {
constructor(executor) {
//默认状态是等待状态
this.status = 'panding';
this.value = undefined;
this.reason = undefined;
//存放成功的回调
this.onResolvedCallbacks = [];
//存放失败的回调
this.onRejectedCallbacks = [];
let resolve = (data) => {//this指的是实例
if (this.status === 'pending') {
this.value = data;
this.status = "resolved";
this.onResolvedCallbacks.forEach(fn => fn());
} }
let reject = (reason) => {
if (this.status === 'pending') {
this.reason = reason;
this.status = 'rejected';
this.onRejectedCallbacks.forEach(fn => fn());
}
}
try {//执行时可能会发生异常
executor(resolve, reject);
} catch (e) {
reject(e);//promise失败了
} }
then(onFuiFilled, onRejected) {
//防止值得穿透
onFuiFilled = typeof onFuiFilled === 'function' ? onFuiFilled : y => y;
onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err; }
let promise2;//作为下一次then方法的promise
if (this.status === 'resolved') {
promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
try {
//成功的逻辑 失败的逻辑
let x = onFuiFilled(this.value);
//看x是不是promise 如果是promise取他的结果 作为promise2成功的的结果
//如果返回一个普通值,作为promise2成功的结果
//resolvePromise可以解析x和promise2之间的关系
//在resolvePromise中传入四个参数,第一个是返回的promise,第二个是返回的结果,第三个和第四个分别是resolve()和reject()的方法。
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e);
}
}, 0)
});
}
if (this.status === 'rejected') {
promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
try {
let x = onRejected(this.reason);
//在resolvePromise中传入四个参数,第一个是返回的promise,第二个是返回的结果,第三个和第四个分别是resolve()和reject()的方法。
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e);
}
}, 0) });
}
//当前既没有完成也没有失败
if (this.status === 'pending') {
promise2 = new Promise((resolve, reject) => {
//把成功的函数一个个存放到成功回调函数数组中
this.onResolvedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onFuiFilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0)
});
//把失败的函数一个个存放到失败回调函数数组中
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
})
})
}
return promise2;//调用then后返回一个新的promise
}
catch(onRejected) {
// catch 方法就是then方法没有成功的简写
return this.then(null, onRejected);
}
}
Promise.all = function (promises) {
//promises是一个promise的数组
return new Promise(function (resolve, reject) {
let arr = []; //arr是最终返回值的结果
let i = 0; // 表示成功了多少次
function processData(index, data) {
arr[index] = data;
if (++i === promises.length) {
resolve(arr);
}
}
for (let i = 0; i < promises.length; i++) {
promises[i].then(function (data) {
processData(i, data)
}, reject)
}
})
}
// 只要有一个promise成功了 就算成功。如果第一个失败了就失败了
Promise.race = function (promises) {
return new Promise((resolve, reject) => {
for (var i = 0; i < promises.length; i++) {
promises[i].then(resolve, reject)
}
})
}
// 生成一个成功的promise
Promise.resolve = function (value) {
return new Promise((resolve, reject) => resolve(value);
}
// 生成一个失败的promise
Promise.reject = function (reason) {
return new Promise((resolve, reject) => reject(reason));
}
module.exports = Promise;

  5、async 和 await相关

  async/await 是 ES7 提出的基于 Promise 的解决异步的最终方案。

  async 就是 generation 和 promise 的语法糖,async 就是将 generator的*换成 async,将 yiled 换成 await函数前必须加一个 async,异步操作方法前加一个 await 关键字,意思就是等一下,执行完了再继续走,注意:await 只能在 async 函数中运行,否则会报错,Promise 如果返回的是一个错误的结果,如果没有做异常处理,就会报错,所以用 try..catch 捕获一下异常就可以了。

  async

async是一个加在函数前的修饰符,被async定义的函数会默认返回一个Promise对象resolve的值。因此对async函数可以直接then,返回值就是then方法传入的函数。使用如下:

        async function fun() {
console.log(1);
return 1;
}
fun().then(val => {
console.log(val) // 1,1
})

  await

  await 也是一个修饰符,只能放在async定义的函数内。可以理解为等待。await 修饰的如果是Promise对象:可以获取Promise中返回的内容(resolve或reject的参数),且取到值后语句才会往下执行;如果不是Promise对象:把这个非promise的东西当做await表达式的结果。使用如下:

        async function fun() {
let a = await new Promise((resolve, reject) => {
setTimeout(function () {
resolve('setTimeout promise')
}, 3000)
})
let b = await "表达式";
let c = await function () {
return '函数表达式'
}()
console.log(a, b, c)
}
fun(); // 3秒后输出:"setTimeout promise" "表达式" "函数表达式"

写在最后

  以上就是本文的全部内容,希望给读者带来些许的帮助和进步,方便的话点个关注,小白的成长之路会持续更新一些工作中常见的问题和技术点。

谁跑的慢,以谁为准执行回调

js--promise、async 和 await 相关知识总结的更多相关文章

  1. ES系列之Promise async 和 await

    概述 promise是异步编程的一种解决方案,比传统的解决方案—回调函数和事件—更合理更强大. 所谓的promise就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作的结果). Pro ...

  2. node.js的async和await

    一.async和await是什么 ES2017 标准引入了 async 函数,使得异步操作变得更加方便,async其实本质是Generator函数的语法糖 async表示函数里有异步操作 await表 ...

  3. JS利用async、await处理少见的登录业务逻辑

    在用uniapp开发一个项目时,有这样一个需求:用户首次登录后,uniapp自动保存用户名密码,之后不管是再次打开项目(打开项目时登录状态已失效)还是 请求接口(接口返回登录失效)时,都会先自动默认的 ...

  4. promise, async和await

    最开始实现异步的方法:回调函数 method1(function(err, result) { if (err) { throw err; } method2(function(err, result ...

  5. js中错误处理的相关知识

    错误bug是指程序执行过程中,导致程序无法正常执行的情况. 后果:程序会强行中断退出:     错误处理:                即使程序出现错误,也保证程序不异常中断的机制. 一般的使用的代 ...

  6. Promise、async、await在Egret的简单应用

    Egret Engnie 5.1.10 Egret Wing 4.1.5 一.Promise.async.await相关知识 Promise介绍 阮一峰 async函数 阮一峰 具体和详细的说明用法可 ...

  7. promise以及async、await学习总结

    Promise/async.await帮我们解决了什么 它给我们提供了一种新的异步编程解决方案,同时避免了困扰已久的回调地狱 // 异步的处理可能会产生这样的回调地狱(第二个异步操作和第一个异步的结果 ...

  8. JavaScript中async和await的使用以及队列问题

    宏任务和微任务的队列入门知识,可以参考之前的文章: JavaScript的事件循环机制 宏任务和微任务在前端面试中,被经常提及到,包括口头和笔试题 async && await概念 a ...

  9. async 和 await 之异步编程的学习

    async修改一个方法,表示其为异步方法.而await表示等待一个异步任务的执行.js方面,在es7中开始得以支持:而.net在c#5.0开始支持.本文章将分别简单介绍他们在js和.net中的基本用法 ...

随机推荐

  1. 集合流之“将List<Integer>转为String并用逗号分割”

    1.使用[流+Collectors]转换 import java.util.ArrayList; import java.util.List; import java.util.stream.Coll ...

  2. 序列化多表操作、请求与响应、视图组件(子类与拓展类)、继承GenericAPIView类重写接口

    今日内容概要 序列化多表操作 请求与相应 视图组件 内容详细 1.序列化多表操作 模型类 models.py中 # 新建django项目 # 创建表 模型类models.py中: from djang ...

  3. ubuntu 安装 mysql mariadb

    本教程面向Ubuntu服务器,适用于Ubuntu的任何LTS版本,包括Ubuntu 14.04,Ubuntu 16.04,Ubuntu 18.04,甚至非LTS版本(如Ubuntu 17.10和其他基 ...

  4. Linux套接子(c语言)模拟http请求、应答

    有关套接子和http请求报文的博客在CSDN有很多比如,点这里查看,这里我就不再做过多赘述了,下面我们直接实战,模拟http请求. 要求:浏览器访问本地的localhost,在浏览器页面打印出 Hel ...

  5. Python的数据基础库Numpy怎样对数组进行排序

    Numpy怎样对数组排序 Numpy给数组排序的三个方法: numpy.sort:返回排序后数组的拷贝 array.sort:原地排序数组而不是返回拷贝 numpy.argsort:间接排序,返回的是 ...

  6. 在网页中预览excel表格文件

    项目需求在前端页面中实现预览excel表格的功能,上网了解之后大致总结为一下几种方法. 1.office文档转换为pdf,再转swf,然后通过网页加载flash进行预览 2.通过 xlsx.js,js ...

  7. Canvas 与 SVG

    什么是SVG? 引用w3c的一段话就是: SVG 指可伸缩矢量图形 (Scalable Vector Graphics) SVG 用来定义用于网络的基于矢量的图形 SVG 使用 XML 格式定义图形 ...

  8. Amaze UI 模版中心上线丨十几款高质量优秀模版免费提供!

    Amaze UI模版中心终于上线了,目前汇聚了包含企业门户.新闻资讯.管理后台等多个领域的模版,全都可以免费下载. Amaze UI模版中心后续还会增加更多的模版以及领域,请各位持续关注. 模版中心的 ...

  9. H5活动全屏滚动页面在安卓智能电视TV调试

    前段时间公司做一个线上活动,在电视上商品促销.产品的要求是每个商品介绍刚好满一屏,按下遥控器向下键可以整屏切换.这种功能如果实在PC端,实现起来非常容易,引用jQuery插件就能实现.但是在安卓智能电 ...

  10. 人机交互大作业---C#WinForm酒店预订系统(纯界面)

    登录: 所有界面: 源代码:最近较忙,后续会上传至github 材料参考:扬中菲尔斯金陵大酒店