学习ES6(九):Promise对象及应用

10 篇文章 0 订阅

一、Javascript的单线程模型

在介绍promise之前,可以提及一下JavaScript最初的设计模式,这样才更好地理解 同步任务异步任务

JavaScript在最初的时候就被设计为单线程模型,为什么呢?
这里举一个例子,如果同时操作dom元素渲染,如果JavaScript是并发执行的,那么可能多个线程都同时处理一个任务,之后的渲染就会出现冲突。JavaScript采用单线程模型,可以有效地控制事件的执行顺序,避免多线程处理出现的问题。
但是又因为JavaScript是单线程,一次只能同时处理一个任务,如果这个任务始终没有执行完成,那后面的任务都不能执行,那么整个系统就会处于死锁状态。所以后面引入了同步任务和异步任务的概念。

简单描述一下单线程工作原理

JavaScript通过维护主任务的执行栈和任务队列,每当接受一个请求,如果是同步事件,就在主任务的执行栈中排队执行,如果是异步任务,就会放入任务队列,然后继续接受请求。如果主任务的执行栈中的任务执行完了之后,就会去循环任务队列,任务队列遵循先进先出的顺序,有效控制了任务的执行顺序,等到该任务处理完之后,再次循环任务队列,重复操作。

二、传统的异步处理

异步setTimeout

我们先来看看setTimeout的作用。

    console.log("1")
    console.log("2")
    console.log("3")

很容易知道上面输出 1 2 3,因为没有异步调用,所以都按顺序执行。
如果在上面的代码中加入setTimeout函数呢?

console.log("1")
setTimeout(() => {
   console.log("2")
}, 1000);
console.log("3")
//输出   1 3 2

为什么呢?和上面提到的任务队列有关,当执行到setTimeout的时候,因为setTimeout是异步任务,所以将setTimeout放入任务队列,然后继续执行下面的代码,直到下面的同步任务执行完之后,开始循环任务队列,发现任务队列里面有一个setTimeout任务,然后才开始执行setTimeout任务。

没有promise的异步处理

在没有使用promise之前,我们需要在需要异步函数的函数的参数中,加入函数名。因为异步函数主要是利用了setTimeout的运行机制。会将要执行的setTimeou任务丢入任务队列,而不是直接执行。

这里我们定义dealasync函数,参数一是输入的数据,参数二是一个函数名。
我们通过setTimeout开启一个异步任务,callback函数用来处理setTimeout处理的结果,因为我们不知道setTimeout任务什么时候结束,所以干脆直接定义一个函数,只要setTimeout处理完成之后有结果,就将结果丢进callback进行处理。

输出结果为 1 3 2 ,2会间隔小段时间输出,因为要等到setTimeout处理完之后才会打印。

    function dealasync(data,callback){
        setTimeout(() => {
            let data2 = data+1
            if(typeof(callback) === 'function'){
                callback(data2)
            }
        }, 2000);
    }
    console.log("1")
    dealasync(1,function(r){
        console.log(`${r}`)
    })
    console.log("3")

那么,如果出现错误怎么办?如何捕捉错误呢??
我们可以使用try…catch…将认为会出错的那部分代码丢进 try 里面,然后用 catch 来捕捉和处理错误。

    function dealasync(val,success,failure){
        setTimeout(() => {
            try{
                if(typeof(val)!=='Number'){
                    throw new Error("it's not belong number")
                }else{
                    let newval = val +1
                    if(typeof(success) === 'function'){
                        success(newval)
                    }
                }
            }
            catch(err){
                if(typeof(failure) === 'function'){
                    failure(err)
                }
            }
        }, 1000);
    }
    -----------------------------------以下代码执行
    dealasync("1", function () {
        console.log("这是数字")
    }, function () {
        console.log("这不是数字")
    })
    // 输出:这不是数字

按照这样的代码结构来书写,每一个函数里面都要写两个函数,一个为成功的回调,一个为失败的回调,如果我们在成功的回调中,再嵌套一层回调,回调依赖回调。那么回调的情况又会进一步复杂。
所以在ES6中,我们引入了Promise!!!!!简直太方便了。

二、Promise

promise是一种异步编程更好的解决办法,比上面的方案更加强大!!

  • promise的优点:对象的状态不受外界影响,一旦状态改变,就不会再变。有三种状态:pending(进行中)、fulfilled(已成功)、rejected(已失败),只有异步操作的结果才可以决定当前是哪一种状态。
  • promise的缺点:一旦新建就会立即执行,无法中途取消。如果你要捕捉错误,你肯定要定义函数来处理,不然抛出的错误不会反应到外部。

resolvereject 是改变promise对象状态的两个方法。
resolve之后,会执行成功的回调。将状态从 pending -> fulfilled
reject之后,会执行失败的回调。将状态从 pending -> rejected

比如:

//直接执行
    console.log("1")
    let  p = new Promise((resolve,reject)=>{
        resolve()
        //直接执行
        console.log("2")
    }).then(()=>{
    //丢入微任务队列
        console.log("3")
    })
    //直接执行
    console.log("4")
    //输出1 2 4 3

这里会涉及到两个概念,分别为宏任务和微任务。自行百度。
执行顺序为:
console.log(“1”)
遇到promise,执行console.log(“2”)
console.log(“4”)
console.log(“3”)
promise新建后立即执行,所以在输出1后就会输出2,然后then方法指定的回调函数,将在当前脚本所有同步任务执行完后才会执行,所以3是最后输出的。

基本用法

1. promise实例生成之后,可以用then方法分别指定resolve状态和rejected状态。

和上面 没有promise异步处理中的代码相似,都会在函数的参数中指定成功和失败的处理函数。

promise.then(function(value){},function(err){})

2. 调用resolve函数和reject函数时带有参数,那么这些参数会传递给回调函数。 如果传的是值,那么在接下来的then当中就可以接收到。

    let p = new Promise((resolve, reject) => {
        reject("1")
    }).then(res => {
        console.log(res)
    }).catch(err => {
        console.log(err)
    })
    //输出为 1

3. 如果有多个promise对象,那么它们的状态可以互相传递,并且互相影响。

不指定 a 状态:

    var a = new Promise((resolve,reject)=>{

    })
    var b = new Promise((resolve,reject)=>{
        resolve(a)
    }).then(res=>{
        console.log("成功")
    }).catch(res=>{
        console.log("失败")
    })
    // 输出为空

指定 a 的状态为 fulfilled。

    var a = new Promise((resolve,reject)=>{
        resolve()
    })
    var b = new Promise((resolve,reject)=>{
        resolve(a)
    }).then(res=>{
        console.log("成功")
    }).catch(res=>{
        console.log("失败")
    })
    //输出为:成功

指定 a 的状态为 rejected。

    var a = new Promise((resolve,reject)=>{
        reject()
    })
    var b = new Promise((resolve,reject)=>{
        resolve(a)
    }).then(res=>{
        console.log("成功")
    }).catch(res=>{
        console.log("失败")
    })
    //输出为:失败

4. 注意:调用resolve和reject并不会终结Promise的参数函数的执行。 一般,调用resolve和reject之后,Promise的使命就完成了,后续操作都应该放在then方法里面,所以最好在它们前面加上return语句,避免意外的发生。

new Promise((resolve,reject)=>{
	return resolve(1)
}).then(res=>{
	//后续操作都在then方法里
)

当然,Promise实例可以链式的then()调用 ,可以一直调用下去。

如果Promise的状态从pending变成rejected。那么直接跳过then语句,直接执行最后的catch方法,捕获异常。

new Promise((resolve,reject)=>{
        console.log("1")
        resolve()
    }).then(()=>{
        console.log("2")
    }).then(()=>{
        console.log("3")
    }).then(()=>{
        console.log("4")
    }).catch(err=>{
        console.log("err错误")
    })

状态变为fulfilled的时候,会一直执行then中的方法,输出 1 2 3 4
如果把状态变为reject。我们来看看

    new Promise((resolve,reject)=>{
        console.log("1")
        reject("1")
    }).then(()=>{
        console.log("2")
    }).then(()=>{
        console.log("3")
    }).then(()=>{
        console.log("4")
    }).catch(()=>{
        console.log("err错误")
    })
    

输出为 1 err错误 。 直接跳过了中间多个then方法,说明当状态变为reject的时候,直接跳到最后的错误处理。

5. 多个promise一起执行。使用promise.all

多个任务同时接受,把多个promise合成同一个promise,接受多个,返回一个。r接受到的是合成多个promise resolve返回的值。
    var p1 = new Promise((resolve,reject)=>{
        setTimeout(() => {
            resolve("p1")
        }, 1000);
    })
    var p2 = new Promise((resolve,reject)=>{
        setTimeout(() => {
            resolve("p2")
        }, 2000);
    })
    var p3 = new Promise((resolve,reject)=>{
        setTimeout(() => {
            resolve("p3")
        }, 3000);
    })
    Promise.all([p1,p2,p3]).then((r)=>{
        console.log(r)
    }).catch((err)=> console.log(err.message))

三、Promise的应用

用promise对象封装的ajax请求

function ajax(url){
        return new Promise((resolve,reject)=>{
            let client = new XMLHttpRequest()
            client.open('GET',url)
            client.onreadystatechange = handler;
            client.responseType = 'json'
            client.setRequestHeader('Accept','application/json')
            client.send()
            function handler(){
                if(this.readyState !==4) return;
                if(this.status ===200){
                    resolve(this.response)
                }else{
                    reject(new Error("JSON not found"))
                }
            }
        })
    }
ajax(url).then(res=>{}).catch(err=>{})
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值