Promise

110 阅读10分钟

作用

  • 解决回调地狱,将异步操作以同步的操作的流程表达出来,避免了层层嵌套的回调函数

优点

  • 可以将异步操作以同步操作的流程表达出来,避免了层层嵌套回调函数
  • Promise对象提供统一接口,使得控制异步操作更加容易

缺点

  • 无法取消promise,一旦新建立就会立即执行,无法中途取消
  • 如果不设置回调函数,promise内部抛出的错误,不会反应到外部
  • 当处于pending状态时,无法得知目前进展到哪一个阶段(刚开始还是刚结束)
Promise.prototype.then

then()方法返回一个Promise,他最多需要两个参数:Promise的成功和失败情况的回调函数

【注意】:如果忽略针对某个状态的回调函数参数,或者提供非函数参数,那么then方法将会丢失关于该状态的回调函数,但是并不会产生错误,如果调用then的promise的状态(fulliment,rejection)发生改变,但是then中并没有关于这种状态的回调函数,那么then将会创建一个没有经过回调函数处理的新的promise对象,这个新的promise只是简单的接收调用这个then的原promise的终态作为它的终态

p.then(onFulfilled[,onReject]);
p.then(value=>{},reason=>{})

onFulfilled

当promise变成接收状态(fulfilled)时调用的函数,该函数有一个参数,即接受的最终结果,如果该参数不是函数,则会在内部被替换为(x)=>x,即原样放回promise最终结果的函数

onReject

当promise变成拒绝状态(rejected)时调用的函数,该函数有一个参数,即拒绝的原因,如果该参数不是函数,则会在内部替换一个thrower函数

返回值

当一个promise完成或失败时,返回函数将被异步调用(由当前的线程来调度完成),具体的返回值依据以下规则返回,如果then中的回调函数

  • 返回一个值,那么then返回的promise将会成为接受状态,并且将返回值作为接受状态的回调函数的参数值
  • 没有返回任何值,那么then返回的promise将会成为接受状态,并且该接受状态的回调函数的参数值为undefined
  • 抛出一个错误,那么then返回的promise将会成为promise将会成为拒绝状态,并且将抛出的错误作为拒绝状态的回调函数的参数值
  • 返回一个已经时接受状态的promise,那么then返回的promise也会成为接受状态,并且将那个promise的接受状态的回调函数的参作为被返回的promise的接受状态回调函数的参数值
  • 返回一个已经是拒绝状态的promise,那么then返回的promise也会成为拒绝状态,并且将那个promise的拒绝状态的回调函数的参数值作为该返回的promise的拒绝状态回调函数的参数值
  • 返回一个未定状态的promise,那么then返回promise的状态也是未定的,并且它的终态与那个promise的终态相同,同时,它变为终态时调用的回调函数参数与那个promise变为终态时的回调函数的参数是相同的
Promise.reject(7).then(
    ()=>console.log(1),
    undefined
    ).then(
        ()=>console.log(2),
        (err)=>console.log(err,3)
    )
//7 3
//第一个promise是reject状态,应该执行then中的第二个回调,但是第二个参数为undefined,不是一个函数,(promsie内部会判断这个回调是不是一个函数,如果是直接执行,如果不是会封装一个简易的h)所以promise内部会封装一个简易的函数来抛出错误,所以这个then返回的也是一个reject的promise
Promise.prototype.catch

catch方法返回一个promise,并且处理拒绝的情况,它的行为与调用promise.prototype.then相同

语法

p.catch(onRejected);

p.catch(function (reason){})
Promise.prototype.finally

finnaly方法返回一个promise,在promise结束时,无论结果是fulfilled或者rejected,都会执行指定的回调函数,这为在promise是否成功完成后都需要执行的代码提供了一种方式,这避免了同样的语句需要在then和catch中各写一次的情况

参数

onFinally

promise结束后调用Function

返回值

返回一个设置了finnally回调函数的promise对象

描述

如果你想在promise执行完毕后无论其结果怎么样都做一些处理或清理时,finally方法可以使用

【与then,catch不同】

  • 调用内联函数时,不需要多次声明该函数或为函数创建一个变量来保存它
  • 由于无法知道promise的最终状态,所以finally的回调函数中不接受任何参数,它仅用于无论最终结果如何都要执行的情况
  • promise.resolve(2).then(()=>{},()={})(resolve的结果为undefined)不同,Promise.resolve(2).finally(()=>{})resolved的结果为2
  • promise.reject(()=>{},()=>{})(rejected的结果为undefined),promsie.reject(3).finally(()=>{})的结果为3
Promise.all

Promise.all()方法接收一个promise类型的输入(Array,Map,Set等都属于ES6的iterable类型)的输入,并且只返回一个promise实例,那个输入的所有的promise的resolve回调的结果是一个数组,这个promise的resolve回调执行只在所有输入的promise的resolve回调都结束,或者输入的iterable里没有promise了的是时候,只要是任何一个输入的promise的reject回调执行或者输入不合法的promise就会立即抛出错误,并且reject的是第一个抛出错误的信息

返回值

  • 如果传入的参数是一个空的可迭代对象,则返回一个已完成状态的promise
  • 如果传入的参数不包含任何promise,则返回一个异步完成的promise
  • 其他情况下返回一个处理中(pending)的promsie,这个返回的promise之后会在所有的promise都完成或有一个promise失败时异步地变为完成或失败,返回值将会按照参数内的promsie顺序排列,而不是由调用promise的完成顺序决定
var resolvedPromisesArray = [Promise.resolve(33), Promise.resolve(44)];
//promsie { <pending> }
//the stack is empty
//promise { [3] }
var resolvedPromisesArray = [33,22];
//Promise { <pending> }
//the stack is empty
//promise { [33,22] }
var resolvedPromisesArray = [];
//Promise { [] } 
//the stack is empty 
//promise { [] }
var p = Promise.all(resolvedPromisesArray);
console.log(p);
setTimeout(function () {
      console.log('the stack is now empty');
      console.log(p);
 );

Promise.race

Promise.race(iterable)方法返回一个promise,一旦迭代器中的某个promise解决或拒绝,返回的promise就会解决或拒绝

返回值

一个待定的promise只要给定迭代中的一个promise解决或拒绝,就次啊用第一个promise的值作为它的值,从而异步的解析或拒绝

描述

race函数返回一个promise,它将与第一个传递的promise相同的完成方式被完成,它可以是完成也可以是失败,这就要取决于第一个完成的方式是两个中的那个

如果可迭代是空的,则返回promise将永远等待

如果迭代包含一个或多个非承诺值和/或已解决/拒绝的承诺,则promise.race将解析为迭代中找到的第一个值

Promise.resolve

promise.resolve(value)方法返回一个以给定值解析后的promise对象,如果这个值是一个promise,那么将返回这个promise,如果这个是是thenable(即带有then方法),返回的promise会跟随这个thenable的对象,采用它的最终状态,否则返回的promise将以此值完成,函数将这类promise的对象的多层嵌套展平

【注意:不要在解析为自身的thenable上调用promise.resolve,这将导致无限递归,应为它视图展平无限嵌套的promise】

let thenable={
    then:(resolve,reject)=>{
        resolve(thenable);
    }
}
Promise.resolve(thenable);//这会造成一个死循环

参数

将被promise对象解析的参数,也可以是一个promise对象,或者是一个thenable

返回值

返回一个带着给定值解析过的promise对象,如果参数本身就是一个promise对象,则直接返回这个promise对象

var original=Promise.resolve(33);
var cast=Promise.resolve(original);
cast.then((value)=>{
    console.log(value);
})
console.log('original===cast?'+(original)===cast)

//orignal === casr ?true
//value:33
Promise.reject

promise.reject返回一个带有拒绝原因的promise

promise.allSettled

promise.allSettled方法返回一个在所有给定的promie都已经fulfilled或rejected后的promise,并带有一个对象数组,每个对象表示对应的promise结果,当由多个彼此不依赖的异步任务成功完成时,或者总是想知道每个promise的结果时,通常使用它

参数

一个可迭代对象

返回值

一旦所指定的promise集合中每一个promise已经完成,无论时成功的达成或拒绝,为决议的promise都将被异步完成,即使,所返回的promise的处理器将传入一个数组作为输入,该数组包含原始promise集中每个promise的结果

对于每个结果对象,都有一个status字符串,如果它的值为fulfilled,则结果对象上存在一个value,如果值为reject,value(或reason)反映了每个promise决议(或拒绝)的值

promise.any

promise.any接收一个promise可迭代对象,只要其中一个promise成功,就返回那个已经成功的promise,如果可迭代对象中没有一个promise成功(即所有的promise都是失败/拒绝),就返回一个失败的promise和AggrefateError类型的实例(时Error的一个子类),用于把单一的错误集合在一起,本质上,这个方法与promise.all是相反的

参数

一个可迭代对象

返回值

  • 如果传入的参数是一个空的可迭代对象,则返回一个已失败状态的promise
  • 如果传入的参数不包含任何promise,则返回一个异步完成的promise
  • 其他情况下都会返回一个处理中的promise,只要传入的迭代对象中任何一个promise便成功状态,或者其中的所有promise都是失败,那么返回的promise就会异步的(当调用栈为空时)变成成功/失败的状态

注意

  • 这个方法用于返回第一个成功的promise,只要一个promise成功此方法就会终止,它不会等待其他的promise全部完成
  • 不想promise.all()会返回一组完成值那样,我们只能得到一个成功之(假设至少有一个promise完成),当我们只需要一个promise成功,而不关心是哪一个成功时这个方法很有用
  • 和promse.race不一样总是返回第一个结果值,这个方法返回的是第一个成功的值,这个方法会忽略掉所有被拒绝的promise,直到第一个promise成功

应用场景

我们有一个获取图片并返回blob函数,我们可以使用promise.any()来获取一些图片并显示第一张有效的图片(即最先resolved的那个promise)

promise手写
//手写promise.all
//当传入的是空数组是返回一个空数组
function all(arr){
    return new Promise((resolve, reject) => {
        const result = new Array(arr.length).fill(false);
        for(let i=0;i<arr.length;i++){
            Promise.resolve(arr[i]).then((data)=>{
                result[i]=data;
                let isfull=result.every(item=>item);
                if(isfull) resolve(result);
            }).catch((err)=>{
                console.log(err);
            })
        }
        resolve([]);
      })
}

//手写promise.race
//当传入的是空的可迭代对象,就一直是pending状态
function race(arr){
    return new Promise((resolve,reject)=>{
        for(const p of arr){
            Promise.resolve(p).then(resolve,reject);
        }
    })
}

//手写promise.allSettled
//返回传入可迭代对象的所有状态
function allSettled(arr){
    let res=new Array(arr.length).fill(false);
    return new Promise((resolve,reject)=>{
        for(let i=0;i<arr.length;i++){
            Promise.resolve(arr[i]).then((data)=>{
                res[i]={status:'fulfilled',value:data};
                res.every(item=>item)&&resolve(res);
            }).catch((err)=>{
                res[i]={status:'reject',reason:err};
                res.every(item=>item)&&resolve(res);
            })
        }
    })
}

//手写promise.any
function any(arr){
    let res=new Array(arr.length).fill(false);
    return new Promise((resolve,reject)=>{
        for(let i=0;i<arr.length;i++){
            Promise.resolve(arr[i]).then((data)=>{
                resolve(data);
            }).catch((err)=>{
                res[i]=err;
                res.every(item=>item)&&reject(res);
            })
        }
        arr.length==0&&reject([]);
    })
}

//测试样例
let array = [
    new Promise((resolve, reject) => {
      setTimeout(resolve, 2000, 10)
    }),
    Promise.resolve(22),
    Promise.resolve(23),
    Promise.resolve(24),
    233,
  ]