promise es6

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_40639990/article/details/89713480

what is promise

MDN 里面有详细的简介
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Using_promises

1.同步和异步

我们知道js的执行环境是单线程的。所谓的单线程,是指js引擎中负责解释和执行js代码的线程只有一个,也就是一次只能完成一项任务,这个任务执行完才能开始下一个,它会阻止其他的任务,就是js代码自上而下的执行,只能等待上面的代码全部执行完事才能开始它的下面的代码的执行。这个任务可称为主线程。但是实际上还有其他的线程,如事件触发线程、ajax请求线程等。这也就引发了同步和异步的问题哦。

1.1 同步

同步模式,就是上面所说的单线程模式, 一次只能执行 一个任务,函数调用后需要等到函数执行结束,返回执行的结果,才能执行下一个任务。如果这个任务执行的时间较长,就会导致 线程阻塞

例1.1

const x = true
while(x);
console.log("don not carry me !!!")

上面的例子就是同步模式,其中的while是一个死循环,它会阻塞进程,因此console.log是在控制台打印不出来的。
同步模式比较简单,也比较容易编写。但问题也显而易见,如果请求的时间比较长,而阻塞了后面代码的执行,体验是很不好的。因此对于一些耗时的操作,异步模式则是更好的选择。

1.2 异步

异步模式,即与同步模式相反,可以一起执行 多个任务,函数调用后不会立即返回执行的结果,如果任务A需要等待,可先执行任务B,等到任务A结果返回后再继续回调。
最常见的模式就是定时器了,我们来看看下面的例子

setTimeout(function(){
        console.log('taskA,asynchronous')
    },0)
console.log('taskB,synchronous')
// while(x);

控制台的输出为
taskB,synchronous
taskA,asynchronous
我们可以看到,定时器延时的时间明明为0,但taskA还是晚于taskB执行。这是为什么呢???由于定时器是异步的, 异步任务会在当前脚本的所有同步任务执行完才会执行。 如果同步代码中含有死循环,即将上面的while的注释去掉,那么这个异步任务就不会执行,因为同步任务阻塞了进程。

1.3回调函数

说到异步,就一定要说一下回调函数了。上面的例子中,setTimeout里面的function便是回调函数。可以简单理解为:(执行完)回(来)调(用)的函数。
回调函数是一段可执行的代码段,它以参数的形式传递给其他代码,在合适的时间执行这段(回调函数)的代码。

回调函数不仅可以用于异步调用,一般同步的场景也可以用回调。在同步调用下,回调函数一般是最后执行的。而在异步调用下,可能一段时间后执行或者步执行。

//同步回调
    var fun1 = function(callback){
        //do something
        //这个地方一定要加分号否则会报错
        console.log('before callback');
        (callback && typeof(callback) === 'function') && callback()
        console.log('after callback')
    }

    var fun2 = function(param){
        // do something
        var start = new Date()
        while((new Date() - start) < 3000){
           
        }
        console.log('i am callback')
    }

    fun1(fun2)

输出结果为:
before callback
i am callback
after callback
由于是同步回调,会阻塞后面的代码,如果fun2是个死循环,后面的代码就不会再执行了。setTimeout是常见的异步回调,另外的一个异步回调即ajax请求。

2.为什么要使用Promise

说完以上的基本概念,我们就可以继续学习Promise
上面提到,Promise对象是用于异步操作的,既然我们可以使用异步回调函数来进行异步操作,为什么还要引入一个promise新概念,还要花时间学习它呢,我们可以看看Promise的过人之处。利用Promise改写异步回调。

 function sendRequest(url,param){
        return new Promise(function(resolve,reject){
            request(url,param,resolve,reject)
        })
    }

    sendRequest('test.html','').then(function(data){
        //异步操作成功后的回调
        console.log('请求成功了,这是返回的数据:',data)
    },function(error) {
        //异步操作失败后的回调
        console.log('请求失败了,这是返回的数据:',error)
    })

这么一看,并没有什么大的区别,还比上面的异步回调复杂,需要先新建Promise再定义其回调。其实,Promise的真正强大之处在于它的多重链式调用,可以避免层层嵌套回调。如果我们在第一次ajax请求后,还要用它返回的结果再次请求呢???

 request('test.html','',function(data1){
        console.log('这是第一次请求成功,这是返回的数据:',data1)
        request('test.html','data1',function(data2){
            console.log('这是第二次请求成功,这是返回的数据:',data2)
            request('test.html','data2',function(data3){
                console.log('这是第三次请求成功,这是返回的数据:',data3)
            },function(error3){
                console.log('这是第三次请求失败,这是返回的数据:',error3)
            })
        },function(error2){
            console.log('这是第二次请求失败,这是返回的数据:',error2)

        })
    },function(error1){
        console.log('这是第一次请求失败,这是返回的数据:',error1)
    })

上面的代码出现了多层回调嵌套,有种晕头转向的感觉,编程体验十分不好,而使用Promise,我们就可以使用then进行链式回调,将异步操作以同步方式操作的流程表示出来。

sendRequest('test.html','').then(function(data1){
        console.log('这是第一次请求成功,这是返回的数据:',data1)
        return sendRequest('test2.html',data1)
    }).then(function(data2){
        console.log('这是第二次请求成功,这是返回的数据:',data2)
        return sendRequest('test3.html',data2)
    }).then(function(data3){
        console.log('这是第三次请求成功,这是返回的数据:',data3)
    }).catch(function(error){
        //使用catch捕获前面的错误
        console.log('这是第一次请求失败,这是失败的信息:',error)
    })

是不是感觉到清晰明显一些,下面我们开始正式进入Promise的学习。

3 Promise的基本用法

3.1 基本用法

上面我们认识了promise长什么样,但对它用到的resolve,rejected,then,catch想必还不了解。

Promise对象代表一个未完成、但是预计将来会完成的操作。
它有以下三种状态:

  • pending:初始值,不是fulfilled,也不是rejected
  • fulfilled:代表操作成功
  • rejected:代表操作失败
    promise有两种状态改变的方式,既可以从pending转变为 fulfilled,也可以从pending转变为rejected。一旦状态改变,就不变了。当状态发生变化,promise.then绑定的函数就会被调用。
    promise一旦新建就会立即执行,无法取消。这是它的缺点之一。
var promise = new Promise(function(resolve,reject){
        if(/*异步操作成功*/){
            resolve(data)
        }else{
            reject(error)
        }
    })

类似构建对象,我们使用new来构建一个 promisepromise接受一个函数作为参数,该函数的两个参数分别是resolve和reject。这两个函数就是回调函数,由js引擎提供。

resolve函数的作用:在异步操作成功时的调用,并将异步的操作结果,作为参数传递出去;
reject函数的作用:在异步操作失败时的调用,并将异步操作报出的错误,作为参数传递出去;

promise实例生成以后,可以用then方法指定resolved状态和reject状态的回调函数。

promise.then(onFulfilled,onRejected);

    promise.then(function(data){
        // do something when success
    },function(error){
        // do something when fail
    })

then方法会返回一个Promise。then就是定义resolve和reject函数的,其resolve参数相当于:

function resolveFun(data){
        // data为promise传出的值
    }

而新建Promise中的’resolve(data)’,则相当于执行resolveFun函数。

Promise新建后就会立即执行。而then方法中指定的回调函数,将在当前脚本所有同步任务执行完才会执行。

var promise = new Promise(function(resolve,reject){
        console.log('before resolved')
        resolve()
        console.log('after resolved')
    })

    promise.then(function(){
        console.log('resolved')
    })

    console.log('outer')

输出顺序:
before resolved
after resolved
outer
resolved

3.2基本API

.then()

语法:promise.prototype.then(onFufilled,onRejected)

对promise添加onFufilled,onRejected回调,并返回的是一个新的promise实例,且返回值将作为参数传入这个新的promise的resolve函数。
.catch()

语法:promise.prototype.then(onRejected)

该方法是.then(undefined,onRejected)的别名,用于指定发生错误时的回调函数。

 promise.then(function(data){
        console.log(success)
    }).catch(function(error){
        console.log(error)
    })

    //等同于
    promise.then(function(data){
        console.log(success)
    }).catch(undefined,function(error){
        console.log(error)
    })
    
var promise = new Promise(function(resolve,reject){
        throw new Error('test')
    })
    //等同于
    var promise = new Promise(function(resolve,reject){
        reject(new Error('test'))
    })

    //用catch捕获
    promise.catch(function(error){
        console.log(error)
    })

输出:test

reject方法的作用就等同于抛错。
promise对象的错误,会一直向后传递,直到被捕获。即错误总会被下一个catch所捕获。then方法指定的回调函数,若抛出错误,也会被下一个catch捕获。catch中也能抛错,则需要后面的catch来捕获。

.all()

语法:Promise.all(iterable)

该方法用于将多个Promise实例,包装成一个新的Promise实例

 var p = Promise.all([p1,p2,p3])

Promise.all方法接受一个数组做参数,数组中的对象(p1,p2,p3)均为promise实例,它的状态将由这3个promise实例来决定。

  • 当p1,p2,p3状态都变成Fulfilled,p的状态才会变成Fulfilled,并将三个promise返回的结果,按参数的顺序(而不是resolve()的顺序)存入数组,传给p的回调函数
var p1 = new Promise(function(resolve,reject){
        setTimeout(resolve,3000,'first')
    })

    var p2 = new Promise(function(resolve,reject){
        resolve('second')
    })

    var p3 = new Promise(function(resolve,reject){
        resolve(resolve,1000,'third')
    })

    Promise.all([p1,p2,p3]).then(function(values){
        console.log(values)
    })

约3秒后
[‘first’,‘second’,‘third’]

  • 当p1,p2,p3其中之一状态变为rejected,p的状态也会变成rejected,并把第一个被reject的promise的返回值,传给p的回调函数。
 var p1 = new Promise(function(resolve,reject){
        setTimeout(resolve,1000,'first')
    })

    var p2 = new Promise(function(resolve,reject){
        resolve(resolve,2000,'second')
    })

    var p3 = new Promise(function(resolve,reject){
        reject('third')
    })

    Promise.all([p1,p2,p3]).then(function(values){
        console.log(values)
    }).catch(function(error){
        console.log(error)
    })

输出:
three

多个promise是同时开始执行的,并行执行的,而不是顺序执行的。

function timePromisefy(delay){
        return new Promise(function(resolve){
            setTimeout(function(){
                resolve(delay);
            },delay)
        })
    }

    var startDate = Date.now()
    Promise.all([
        timePromisefy(1),
        timePromisefy(32),
        timePromisefy(64),
        timePromisefy(128),
        ]).then(function(values){
            console.log(Date.now() - startDate +'ms')
        })

最后输出为133ms //会大于128,但不是所有的时间总和

.resolve()

Promise.resolve(value)
Promise.resolve(promise)
Promise.resolve(thenable)

它可以看作是new Promise()的快捷方式

Promise.resolve('success')
//等同于
new Promise(function(resolve){
    resolve('success')
  })
         

.reject()

Promise.reject(reason)

它可以看作是new Promise()的快捷方式

Promise.resolve('success')
//等同于
new Promise(function(resolve,reject){
    reject(new Error('error'))
  })
         

4.总结

创建promise的流程:
1.使用new Promise(fn)或者它的快捷方式Promise.resolve()、Promise.reject(),返回一个promise对象
2.在fn中指定异步的处理
处理结果正常,调用resolve
处理结果错误,调用reject

最后希望大家能够有所收获!!!

猜你喜欢

转载自blog.csdn.net/qq_40639990/article/details/89713480