javascript中的糖衣语法Promise对象详解

目录
  • 一、Promise的诞生
    • 1、回调地狱
  • 二、Promise的行为
    • 1、Promise的语法
    • 2、Promise的方法
    • (1)Promise.prototype.then()
    • (2)Promise.prototype.catch()
    • (3)Promise.prototype.finally()
    • (4)Promise.resolve()
    • (5)Promise.reject()
    • (6)Promise.all()
    • (7)Promise.race()
  • 三、Promise的场景
    • 1、Ajax请求
    • 2、文件读取
    • 3、图片加载
    • 4、函数封装
  • 四、Promise的短板
  • 总结

知道Promise对象的你,知道在使用Promise对象的时候,我们如何做到在使用中如鱼得水嘛?10分钟带你迅速上手!

一、Promise的诞生

1、回调地狱

最初javascript的异步实现就是使用回调函数。回调地狱就是:一个函数需要等它的回调函数(或者回调和回调的回调...)执行完毕之后再执行。简单来说,回调函数里面嵌套回调函数。而因为回调地狱的问题,Promise就出现了。我们看看什么是回调地狱:

// 回调地狱
//地狱回调
setTimeout(function () {  //第一层
    console.log(1);//等4秒打印1,然后执行下一个回调函数
    setTimeout(function () {  //第二层
        console.log(2);//等3秒打印2,然后执行下一个回调函数
        setTimeout(function () {   //第三层
            console.log(3);//等2秒打印3,然后执行下一个回调函数
            setTimeout(function () {   //第四层
                console.log(4);//等1秒打印4
            }, 1000)
        }, 2000)
    }, 3000)
}, 4000)

可看出回调地狱的特点

1.难以复用
2.堆栈信息被断开
3.借助外层变量

回调地狱是为了让我们代码执行顺序的一种操作(解决异步),但是它会使我们的可读性非常差。当你有100个,1000个...,代码会不断右移,不够优雅,也会影响性能。嵌套和缩进只是回调地狱的一个梗而已,它导致的问题远非嵌套导致的可读性降低而已。接下里我们今天的目的,就是将上面的回调地狱用Promise解决。

二、Promise的行为

1、Promise的语法

Promise的基本语法:Promise函数接收一个函数作为参数,这个函数有两个参数,一个是成功函数(resolve),一个是失败函数(reject)。Promise的.then接收两个回调函数,一个是成功函数的回调,一个是失败函数的回调。这两个函数可选,不一定要提供

//Promise语法
let myPromise = new Promise(function(resolve, reject) {
    // "Producing Code"(可能需要一些时间)

      resolve(); // 成功时
      reject();  // 出错时
    });

    // "Consuming Code" (必须等待一个兑现的承诺)
    myPromise.then(
      function(value) { /* 成功时的代码 */ },
      function(error) { /* 出错时的代码 */ }
    );

特别注意:

(1)Promise对象中的状态不会被外界干扰。状态的改变取决于异步的操作结果。

(2)Promise对象的状态一旦被改变,就不会进行再次改变。 例如:

let myPromise = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('ok');//第一次状态为成功
        reject('no');//不会改变
    })
}).then(
    function (result) { console.log('resolved'); },//成功状态执行then后面的成功回调函数
    function (error) { console.log('reject'); }
)
//resolved

(3)Promise新建后就会立即执行,Promise后面的.then是一个异步操作,在事件循环中叫做“微任务”。会放在同步代码后面执行。 例如:

let myPromise=new Promise((resolve,reject)=>{
    console.log('Promise');//1
    resolve();
})
    .then(()=>{//这里是一个异步操作
        console.log('succeed');//3
    })
console.log('Promise resolved');//2
// Promise
// Promise resolved
// succeed

2、Promise的方法

(1)Promise.prototype.then()

then方法的返回结果是新的Promise实例,对象状态由回调函数的执行结果决定。then方法后面还可以再调用另一个then方法,形成链条。采用链式的then,可以指定一组按照次序调用的回调函数。

const p = new Promise((resolve, reject) => {
    setTimeout(() => {
        //设置 p 对象的状态为失败,并设置失败的值
        reject("出错啦!");
    }, 1000)
});
p.then(
    function(value){},
    function(reason){console.log(reason);}
)
.then(()=>{
    console.log(123)
});

(2)Promise.prototype.catch()

catch()用于指定发生错误时的回调函数.例如:

const p = new Promise((resolve, reject) => {//p为Promise的实例
    setTimeout(() => {
        //设置 p 对象的状态为失败,并设置失败的值
        reject("出错啦!");//reject()方法的作用,等同于抛出错误
    }, 1000)
});
// p.then(function(value){},function(reason){
// // console.error(reason);
// console.log(reason);
// });
​
p.catch(function (reason) {//相当于上面的.then(...)
    console.log(reason);//捕获reject状态的值
});

建议总是使用catch()方法,而不使用then()方法的第二个参数.

catch()还可以这样使用:

const myPromise = new Promise(function(resolve, reject) {
    throw new Error('出错啦');//从这抛出错误,catch()指定的回调函数也可以捕获
  });
  promise.catch(function(error) {
    console.log(error);
});

(3)Promise.prototype.finally()

finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。而且finally方法总是会返回原来的值。举个例子:

function a(){
    return new Promise((resolve,reject)=>{
        resolve('ok')
    },1000)
}
function b(){
    return new Promise((resolve,reject)=>{
        setTimeout(()=>{
            reject('no')
        },1500)
    })
}
function c(){
    setTimeout(()=>{
         console.log('finally');
    },500)

}
a()
    .then((res)=>{
        console.log(res);
        return b()
    })
    .catch((err)=>{
        console.log(err);
    })
    .finally(c)//参数不是回调函数,如果是回调函数也不需要带参
​
//ok
//no
//finally //说明finally()照样执行,有打印

(4)Promise.resolve()

将现有对象转为 Promise 对象,状态为resolved。举例如下:

let myString='hello';
console.log(myString);//hello
const myPromise=Promise.resolve(myString)//带参
//等同于const myPromise=Promise.resolve('hello')
console.log(myPromise);//Promise { 'hello' }
myString=Promise.resolve();//不带参,直接调用
console.log(myString);//Promise { undefined }
myString.then(result=>{//说明myString已经是Promise对象了,只有Promise对象才有.then
    console.log(result);//undefined
})

(5)Promise.reject()

也会返回一个新的 Promise 实例,该实例的状态为rejected。简单举例:

// 以上代码等于
const p=new Promise((resolve,reject)=>{
    reject('error')
})
p.catch(error=>{
    console.log(error);//error
})
// 或者
p.then(function(){},function(error){console.log(error);})//error
// 或者
p.then(null,function(error){console.log(error);})//error
// 或者
p.then(undefined,function(error){console.log(error);})//error

(6)Promise.all()

all()是将多个 Promise 实例,包装成一个新的 Promise 实例。接收一个数组作为参数,数组的每一项都是·Promise对象的实例。如果不是,会通过Promise.resolve()将参数转为Promise实例,再进行处理。all()用于将多个 Promise 实例,包装成一个新的 Promise 实例

// promise.all()
const myPromise1=new Promise((resolve,reject)=>{
    resolve('sure');
})
    .then(result=>result)

const myPromise2=new Promise((resolve,reject)=>{
    reject('cancel')
})
    .then(result=>result)
    .catch(error=>error)//myPromise2有自己的catch
//感兴趣的小伙伴可以尝试,如果删除myPromise2.catch(...)后Promise.all([myPromise1,myPromise2])会如何?

Promise.all([myPromise1,myPromise2])//myPromise1,myPromise2都处于成功状态
    .then(result=>{console.log(result);})//走这里  [ 'sure', 'cancel' ]
    .catch(error=>{console.log(error);})

(7)Promise.race()

race()是将多个 Promise 实例,包装成一个新的 Promise 实例。接收一个数组作为参数,数组的每一项都是·Promise对象的实例。如果不是,会通过promise.resolve()将参数转为Promise实例,再进行处理。只要参数的Promise实例有一个率先改变状态,则状态改变。例如:

const myPromise2 = new Promise((resolve, reject) => {
    setTimeout(()=>{
       reject('cancel')
    },3000)
    //如果将时间改为<2000,Promise.race([myPromise1, myPromise2])走哪一步呢?

})
    .then(result => result)
    .catch(error => error)
const myPromise1 = new Promise((resolve, reject) => {
    setTimeout(()=>{
        resolve('sure');
    },2000)//myPromise1比myPromise2更先改变状态

})
    .then(result => result)
​
​
Promise.race([myPromise1, myPromise2])
    .then(result => { console.log(result); })//走这里,sure
    .catch(error => { console.log(error); })

简要说一下const p1=Promise.all([promise1,promise2,promise3]) 和const p2=Promise.race([promise1,promise2,promise3])的区别

--前者Promise的实例状态都为resolved时,p1调用.then()里面成功时的回调函数;如果实例状态有一个为rejected,p1调用.then()里面失败时的函数或者走.catch()

--后者注重时序,如果率先改变状态的实例为resolved,则p2为reslove状态;否则,为reject状态。

三、Promise的场景

1、Ajax请求

<head>
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
</head>
<body>
   <div class="name">
      <audio id="audio" controls></audio>
   </div>

   <script>
      function getSong() {
       return new Promise((resolve, reject) => {
       $.ajax({
        url: 'https://www.fastmock.site/mock/c024e8920dd6003c63dcd9ed2bbf6cb9/music/music',
        dataType: 'json',
        success(res) {
            console.log(res);
            url = res[0].url;
        }
    })
      resolve();
    })
  }

            function playSong() {
                let audio = document.getElementById('audio');
                window.addEventListener('click', function () {
                audio.src = url;
                audio.play()
            })

            }
            getSong().then(playSong())
    </script>
</body>

2、文件读取

// 引入fs模块
const fs=require('fs');
// 使用Promise封装
const P=new Promise(function(resolve,reject){
    fs.readFile('./text/2.md',(err,data)=>{
        // 如果地址错误,抛出异常
        if(err) reject(err) ;

        // 如果成功,输出内容
        resolve(data);
    });
});

P.then(function(value){
    console.log(value.toString());
},function(reason){
    console.log("defeat!!!!");
});

3、图片加载

<body>
    <img src="http://m.imeitou.com/uploads/allimg/220514/5-220514101036.jpg" alt="风景" id="myImage">

    <script>
        const preloadImage = function (path) {
            return new Promise(function (resolve, reject) {
                const image = new Image();
                // 图片加载成功
                image.onload=()=>{
                    resolve(image)
                }
                // 图片加载失败
                image.onerror=()=>{
                    reject('sorry,cannot loding picture')
                };
                image.src = path;
            });
        };

        // 获取图片DOM节点
        var preImage=document.getElementById('myImage')
        // 图片预加载
        preloadImage('http://m.imeitou.com/uploads/allimg/220512/5-220512111J0-50.jpg')
        .then(targetImage=>
            // 点击页面切换图片,让图片加载
            window.onclick=function(){
                setTimeout(()=>{
                       preImage.src=targetImage.src
                },1000)
            }
        )

    </script>
</body>

4、函数封装

//例如将微信小程序里的showModal进行Promise封装,那么在任何需要用到此函数的直接引入就很方便了
// promise形式 封装 showModal
export const showModal=({content})=>{
    return new Promise((resolve,reject)=>{
        wx.showModal({
          title:'提示',
          content:content,
            success: (result) => {
                resolve(result);
            },
            fail: (err) => {
                reject(err);
            }
        });

    })
}

四、Promise的短板

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

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

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

总结

恭喜你看到文章末尾了,相信你可以写出Promise方案解决第一个回调地狱的异步问题了,快去试试把!

到此这篇关于javascript中的糖衣语法Promise对象详解的文章就介绍到这了,更多相关js promise对象内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 详解JavaScript异步编程中jQuery的promise对象的作用

    Promise, 中文可以理解为愿望,代表单个操作完成的最终结果.一个Promise拥有三种状态:分别是unfulfilled(未满足的).fulfilled(满足的).failed(失败的),fulfilled状态和failed状态都可以被监听.一个愿望可以从未满足状态变为满足或者失败状态,一旦一个愿望处于满足或者失败状态,其状态将不可再变化.这种"不可改变"的特性对于一个Promise来说非常的重要,它可以避免Promise的状态监听器修改一个Promise的状态导致别的监听器的行

  • 浅谈JavaScript中的对象及Promise对象的实现

    JavaScript 中的所有事物都是对象:字符串.数值.数组.函数.下面小编给大家收集整理些javascript中的对象及promise对象的实现.具体内容如下: 到处都是对象 window对象 常用的属性和方法介绍 location 包含页面的URL,如果改变这个属性,浏览器会访问新的URL status 包含将在浏览器状态去显示的一个串.一般在浏览器左下角 onload: 包含了需要在页面完全加载后调用的函数 document: 包含DOM alert方法: 显示一个提醒 prompt方法

  • javascript使用Promise对象实现异步编程

    Promise对象是CommonJS工作组为异步编程提供的统一接口,是ECMAScript6中提供了对Promise的原生支持,Promise就是在未来发生的事情,使用Promise可以避免回调函数的层层嵌套,还提供了规范更加容易的对异步操作进行控制.提供了reject,resolve,then和catch等方法. 使用PROMISE Promise是ES6之后原生的对象,我们只需要实例化Promise对象就可以直接使用. 实例化Promise: var promise = new Promis

  • Javascript的异步函数和Promise对象你了解吗

    目录 1.JS中的异步 1.1同步 1.2异步 1.3回调函数解决异步问题 1.4回调地狱 2.Promise对象 2.1Promise的基本使用 2.2async和await 总结 1.JS中的异步 1.1 同步 一般情况下,js的代码都是自上而下顺序运行的.例如: let res = ''; res = '获取到的结果!'; console.log(res); 结果: 很容易理解,我给res赋了新值,然后输出res.这就是js的同步执行,这里的同步,并不是一起执行的意思,而是在一个线程里顺序

  • JS异步编程Promise对象详解

    1.单线程模型 单线程模型指的是,JavaScript 只在一个线程上运行.也就是说,JavaScript 同时只能执行一个任务,其他任务都必须在后面排队等待.注意,JavaScript 只在一个线程上运行,不代表 JavaScript 引擎只有一个线程.事实上,JavaScript 引擎有多个线程,单个脚本只能在一个线程上运行(称为主线程),其他线程都是在后台配合. JavaScript 之所以采用单线程,而不是多线程,跟历史有关系.JavaScript 从诞生起就是单线程,原因是不想让浏览器

  • javascript中的糖衣语法Promise对象详解

    目录 一.Promise的诞生 1.回调地狱 二.Promise的行为 1.Promise的语法 2.Promise的方法 (1)Promise.prototype.then() (2)Promise.prototype.catch() (3)Promise.prototype.finally() (4)Promise.resolve() (5)Promise.reject() (6)Promise.all() (7)Promise.race() 三.Promise的场景 1.Ajax请求 2.

  • JavaScript中SetInterval与setTimeout的用法详解

    setTimeout 描述 setTimeout(code,millisec) setTimeout() 方法用于在指定的毫秒数后调用函数或计算表达式. 注:调用过程中,可以使用clearTimeout(id_of_settimeout)终止 参数 描述 code 必需,要调用的函数后要执行的 JavaScript 代码串. millisec 必需,在执行代码前需等待的毫秒数. setTimeinterval setInterval(code,millisec[,"lang"]) 参数

  • Javascript中的迭代、归并方法详解

    迭代方法 在Javascript中迭代方法个人觉得尤为重要,在很多时候都会有实际上的需求,javascript提供了5个迭代方法来供我们操作,它们分别为: every() 对数组中的每一个项运用给定的函数,如果每项都返回true,那么就会返回true filter() 对数组中的每一个项运用给定的函数,把返回true的项组成一个新数组并返回 forEach() 对数组中的每一项运用给定的函数,但是没有任何的返回值 map() 对数组中的每一个项运用给定的函数并返回每次函数调用的结果组成新的数组

  • JavaScript中闭包的写法和作用详解

    1.什么是闭包 闭包是有权访问另一个函数作用域的变量的函数. 简单的说,Javascript允许使用内部函数---即函数定义和函数表达式位于另一个函数的函数体内.而且,这些内部函数可以访问它们所在的外部函数中声明的所有局部变量.参数和声明的其他内部函数.当其中一个这样的内部函数在包含它们的外部函数之外被调用时,就会形成闭包. 2.变量的作用域 要理解闭包,首先要理解变量的作用域. 变量的作用域无非就是两种:全局变量和局部变量. Javascript语言的特殊之处,就在于函数内部可以直接读取全局变

  • JavaScript中关键字 in 的使用方法详解

    for-in循环应该用在非数组对象的遍历上,使用for-in进行循环也被称为"枚举". 对于数组 ,迭代出来的是数组元素 但不推荐,因为不能保证顺序,而且如果在Array的原型上添加了属性,这个属性也会被遍历出来,所以 最好数组使用正常的for循环,对象使用for-in循环 对于对象 ,迭代出来的是对象的属性: var obj = { "key1":"value1", "key2":"value2", &q

  • javascript中href和replace的比较(详解)

    在使用javascript的时候,有时候对于经常使用的方法太熟悉而忽略了他们之间原理的细微差别. 举例如下: window.location.href,window.location.replace. 这两种方式都可以让页面跳转到一个新的页面,但是其中我就忽略了跳转之后的细节,比如返回的原来的页面. window.location.href中的href其实就是<a>标签中的href,使用这个进行页面跳转后,可以使用浏览器的后退按钮退回到原来的页面,也可以使用history.go(-1)函数跳转

  • JavaScript中object和Object的区别(详解)

    JavaScript中object和Object有什么区别,为什么用typeof检测对象,返回object,而用instanceof 必须要接Object呢 这个问题和我之前遇到的问题非常相似,我认为这里有两个问题需要解决,一个是运算符new的作用机制,一个是function关键字和Funtion内置对象之间的区别.看了一些前辈的博客和标准,这里帮提问者总结一下. 1.new new运算符的作用是创建一个对象实例.这个对象可以是用户自定义的,也可以是带构造函数的一些系统自带的对象.如果 new

  • 基于javascript中的typeof和类型判断(详解)

    typeof ECMAScript 有 5 种原始类型(primitive type),即 Undefined.Null.Boolean.Number 和 String.我们都知道可以使用typeof运算符求得一个变量的类型,但是对引用类型变量却只会返回object,也就是说typeof只能正确识别基本类型值变量. var a = "abc"; console.log(typeof a); // "string" var b = 123; console.log(t

  • javascript中call,apply,bind的区别详解

    在JS中,这三者都是用来改变函数的this对象的指向的,他们有什么样的区别呢. 在说区别之前还是先总结一下三者的相似之处: 1.都是用来改变函数的this对象的指向的. 2.第一个参数都是this要指向的对象. 3.都可以利用后续参数传参. 那么他们的区别在哪里的,先看一个例子. var xw = { name : "小王", gender : "男", age : 24, say : function() { alert(this.name + " ,

  • 在JavaScript中对HTML进行反转义详解

    在JavaScript中对字符串进行转义和反转义操作,常用的方法莫过于使用encodeURI (decodeURI).encodeURIComponent (decodeURIComponent)这几个方法,具体使用方法和区别. 但是如何在JavaScript中对HTML进行反转义操作呢?例如下面这段代码: var jsonData = { title: "<%= data.name? data.name : title %>", desc: "<%= da

随机推荐