前端异步解决方案-3(Promise)
又有好些天没有动笔了,这几天一直在断断续续的学习Promise和generator,今天终于能够把着两个玩意结合起来了解决异步问题了。今天我先把promise相关的用法和对异步的处理分享给大家。
老样子,还是先模拟一个Promise。
//咳咳这样就实现了嘛 let MyPromise = Promise;
开个玩笑,其实这两天我也一直在看Promise的实现,但是还是没有怎么理解。所以Promise的代码实现我暂时先放一放,等我完全理解再来新开一篇分享。这里先给大家推荐几篇我觉得比较好的Promise实现的博客,想要学习的小伙伴可以先到那边一睹为快,当然等我更新了之后大家还是要来给我文章点赞的哈。
彻底理解Promise对象——用es5语法实现一个自己的Promise(上篇)这一篇文章用的es5的语法,对es6不是很熟悉的同学可以用这篇文章顶一下
Promise实现原理(附源码)这一篇呢是用到了class的,所以需要大家对es6有所了解,也是一篇好文章
接下来我介绍一下Promise
最常用的几个部分:
首先先介绍最简单的只有一个
Promise
对象的用法//Promise()接收一个函数,并且在创建这个Promise对象的同时,接收的这个函数就会被立刻执行 var promise = new Promise(function (resolve, reject) { //resolve用来接收成功的返回,reject用来接收失败的返回 setTimeout(function () { //这里我们生成一个随机数,并在接下来根据这个随机数的大小来判断这个异步是否成功 let num = Math.random(); if (num > 0.8) { //接收失败的原因 console.log("reject") reject(num + "大于0.8,这次异步操作失败了") } else { //接收成功的数据 console.log("resolve") resolve(num + "小于0.8,这次异步操作成功了") } }, 100) }); console.log("一个Promise对象生成了"); //Promise对象的.then()方法接收两个回调,第一个为成功回调,第二个为失败回调 //这两个回调会在上面的resolve或者reject函数生效后被调用 promise.then(function (data) { //这个函数会在上面的resolve函数被调用后生效,这里的data就是上面resolve()中接收的值 console.log(data) }, function (err) { ///这个函数会在上面的reject函数被调用后生效,这里的err就是上面reject()中接收的值 console.error(err) }); console.log("promise的.then方法被调用了")
大家可以按F12唤起控制台,然后把这段代码运行几次。看看会有什么结果;
多运行几次以后,大家应该可以看到这两类结果:
其中的undefind是console.log("promise的.then方法被调用了")
这行代码的返回,大家可以不用关注。在这里可以看到不论是成功的结果还是失败的结果都是在定时器执行后再打印出来的。这种写法可以帮助我们实现简单的异步编程。接下介绍多个
Promise
对象同时使用的用法, 先介绍最常见的.then()链式调用的方法//用来快速生成一个Promise对象,接收一个日志列表,不论成功还是失败都会往日志列表中添加一条日志 function promise(log) { return new Promise(function (resolve, reject) { setTimeout(function () { log = log || []; //和上次的例子一样,利用随机数来随机失败和成功 let num = Math.random(); if (num > 0.5) { log.push(num + "大于0.5,这次异步操作失败了"); reject(log) } else { log.push(num + "小于0.5,这次异步操作成功了"); resolve(log) } }, 100) }) }
.then()中返回了Promise对象的情况
var promise1 = promise(); //promise1.then()方法会返回一个Promise对象 //如果我们在.then()生效的那个 !!!回调方法!!! 中有返回一个Promise对象的话,该对象会被 !!!.then()方法!!! 返回 //先看返回了Promise对象的方式 promise1.then(function (data) { console.log(data); return promise(data) }, function (err) { console.error(err); return promise(err) }).then(function (data) { console.log(data) }, function (err) { console.error(err) });
这段代码运行后一共会有四种结果:
两次都成功
两次都失败
第一次失败,第二次成功
第一次成功,第二次失败通过这种方法我们可以用比较清晰的方式来书写我们的异步代码。特别是多个异步操作嵌套的时候,可以链式调用.then()来实现,这样的代码看起来逻辑更清晰;
刚刚看完了返回了Promise对象的场景,再来看看没有返回Promise的场景//如果我们没有返回Promise对象,.then()就会将我们返回的东西包装成一个Promise对象(没有返回就相当于返回了undefined) //可以等同于我们写了 return new Promise((resolve,reject)=>{resolve(/*原先的返回值*/)}) promise1.then(function (data) { console.log(data); return data; }, function (err) { console.error(err); return err; }).then(function (data) { console.log(data) }, function (err) { //这里是永远不会被触发的,原因是上一个.then() 返回的是new Promise((resolve,reject)=>{resolve(/*原先的返回值*/)}) //返回的Promise对象的reject方法永远都不会被触发,所以这个里也就永远都不会触发了 console.error(err) });
讲解都写在注释里面了,接下里我就贴运行图吧,这段代码会运行出以下两类结果:
需要所有的请求都返回后才可以执行某个动作
//改造promise,让其可以接收一个定时器等待时间参数 function promise(log, time) { return new Promise(function (resolve, reject) { setTimeout(function () { log = log || []; //和上次的例子一样,利用随机数来随机失败和成功 let num = Math.random(); if (num > 0.5) { log.push("等待时长" + time + "," + num + "大于0.5,这次异步操作失败了"); console.error(log); reject(log) } else { log.push("等待时长" + time + "," + num + "小于0.5,这次异步操作成功了"); console.log(log); resolve(log) } }, time) }) } //Promise.all()可以接收一个Promise对象的数组,返回一个Promise对象 //该Promise对象会在数组中所有Promise成功返回后执行成功回调,在任意一个Promise失败后立刻执行失败回调 var promise1 = promise(null, 10), promise2 = promise(null, 100), promise3 = Promise.all([promise1, promise2]); promise3.then((data) => { //这里的data为promise1,和promise2的返回值的数组 console.log("promise3", data) }, (err, err2) => { //报错信息 console.error("promise3", err) });
这段代码一共有四种可能的结果
如果两次都成功的话
如果两次都成的话,promise3会执行成功回调,并且回调中的data就是promise1和promise2返回值的数组(数组顺序和.all()中的顺序一致)
任意一个promise失败的话,promise3会立刻执行失败回调,并且回调中的err就是失败的那个promise在reject中返回的值
文章写到这里,我认为Promise常用的一些用法都已经讲完了,更详细的Promise的教程请参考 MDN中对promise的讲解