promise

问题:

 一个页面 进入页面会有四个接口请求,要求每个请求都带loading,请求完 loading消失。从页面加载速度,和资源浪费 两方面考虑, 用promise 如何实现

内容:

1、promise的含义
2、基本用法
3、 prototype.then()
4、prototype.catch()
5、Promise.reslove()
6、Promise.reject()
7、prototype.finally()
8、Promise.all()
9、Promise.any()
10、Promise.allSettled()
11、Promise.race()
12、Promise.try()
13、应用
一、promise的含义

自我定义:处理异步的一个手段,将异步操作改成同步操作的形式 

标准定义:

Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。

所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。

特点: 

三种状态: reslove(成功)  reject(失败) pending(初始化/进行中)

状态只能是从pending ===>reslove/reject    并且状态已经改变,不会在改了

eg.

const promise = new Promise(function(resolve, reject) {

  resolve('ok');

  reject('no');

});

promise

    .then(function(value) { console.log(value) })

    .catch(function(error) { console.log(error) });

// ok

缺点:

1、无法被取消,创建就会立即执行,中途不会取消,

let promise = new Promise(function(resolve, reject) {

  console.log('Promise');

  resolve();

});


promise.then(function() {

  console.log('resolved.');
  
});

console.log('Hi!');

// Promise

// Hi!

// resolved  

2、如果不设置回调函数Promise内部的错误不会反应到外部,可以把内部错误给‘吃掉’

const someAsyncThing = function() {

  return new Promise(function(resolve, reject) {

    // 下面一行会报错,因为x没有声明

    resolve(x + 2);

  });};

someAsyncThing().then(function() {

  console.log('everything is great');});

setTimeout(() => { console.log(123) }, 2000);

// Uncaught (in promise) ReferenceError: x is not defined

// 123

3、 当处于pending状态时,无法得知目前进展到哪一个阶段

二、基本用法

function fn() {

  return new Promise((resolve, reject) => {

    if(true) {

      resolve(true)

    }else {

      reject(false)

    }

  })

}

fn.then(res => {

  //成功

}).catch(oError => {

  //失败

})


fn.then(res => {

  //成功

},oError => {

  //失败

})
const p1 = new Promise(function (resolve, reject) {

  setTimeout(() => reject(new Error('fail')), 3000)
  
})


const p2 = new Promise(function (resolve, reject) {

  setTimeout(() => resolve(p1), 1000)
  
})

p2

  .then(result => console.log(result))

  .catch(error => console.log(error))

// Error: fail
三、prototype.then()

Promise 实例添加状态改变时的回调函数,then(reslove,reject),两个参数,第二个可以不写

四、prototype.catch()

用于指定发生错误时的回调函数 catch其实是对应then的第二个参数 then('',catch)

// 写法一

const promise = new Promise(function(resolve, reject) {

  try {

    throw new Error('test');

  } catch(e) {

    reject(e);

  }});

promise.catch(function(error) {

  console.log(error);});

// 写法二

const promise = new Promise(function(resolve, reject) {

  reject(new Error('test'));});

promise.catch(function(error) {

  console.log(error);});

Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获。

function p1() {

  return new Promise((resolve, reject) => {

    resolve(111)

  })

}

function p2() {

  return new Promise((resolve, reject) => {

    reject(2222222)

  })

}

function p3() {

  return new Promise((resolve, reject) => {

    resolve(3333)

  })

}

p1().then(p2).then(p3).then(res => {

  console.log(res);

}).catch(e => {

  console.log(e)

})

p1().then(function () {

  p2().then(function () {

    p3().then(res=> {

      console.log(res);

    }).catch(e=> {

      console.log(e);

    })

  }).catch(e=> {

    console.log(e);

  })

})
五、Promise.reslove()  转化成Promise对象

Promise.resolve('foo')

// 等价于

new Promise(resolve => resolve('foo'))

    1、如果参数是Promise对象 ,不做任何处理  直接返回

2、参数是具有then方法的对象。

let thenable = {

  then: function(resolve, reject) {

    resolve(42);

  }};

会将他转化成Promise对象,然后立即执行对象的then方法

let thenable = {

  then: function(resolve, reject) {

    resolve(42);

  }

};

let p1 = Promise.resolve(thenable);

p1.then(function(value) {

  console.log(value);  // 42

});
let thenable = {

  then: function(resolve, reject) {

    reject(42);

  }

};

let p1 = Promise.resolve(thenable);

console.log(p1);

p1.then(function(value) {

  console.log(value);

}, oError=> {

  console.log(oError); //42

});

3、参数没有then方法或者不是对象   ------  返回一个新的 Promise 对象,状态为resolved

let p = Promise.resolve('hello')

console.log(p);

p.then(res=>{

  console.log(res);

})

4、参数为空  ----  会直接返回一个Promise对象

const p = Promise.resolve();

p.then(function () {

  // ...

});

then是在本轮“事件循环”(event loop)的结束时执行,而不是在下一轮“事件循环”的开始时

setTimeout(function () {

  console.log('three');
  
}, 0)

Promise.resolve().then(function () {

  console.log('two');
  
});

console.log('one');

// one

// two

// three
六、Promise.reject()

方法也会返回一个新的 Promise 实例,该实例的状态为rejected。 原封不动地作为reject的理由,变成后续方法的参数

const thenable = {

  then(resolve, reject) {

    reject('出错了');

  }};


Promise.reject(thenable).catch(e => {

  console.log(e === thenable)})

// true
七、prototype.finally()

finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作

promise

.finally(() => {

  // 语句

});

// 等同于

promise

.then(

  result => {

    // 语句

    return result;

  },

  error => {

    // 语句

    throw error;

  });
Promise.prototype.finally = function (callback) {

  let P = this.constructor;

  return this.then(

    value  => P.resolve(callback()).then(() => value),

    reason => P.resolve(callback()).then(() => { throw reason })

  );};
八、Promise.all()  都成功才成功,一个失败就失败

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

如果参数不是Promise对象会先变成Promise对象  通过Promise.reslove()方法

Promise.all()方法的参数可以不是数组,但必须具有 Iterator 接口  可循环的数据  for ... of  

Array  

Object  

Map([['one', 1], ['two', 2]]) 

Set([1, 1, 2, 2, 1])
let p1 = 'hello'

let p2 = new Promise((resolve, reject) => {

  setTimeout(function () {

    resolve(222)

  },3100)

})

let p3 = new Promise((resolve, reject) => {

  setTimeout(function () {

    resolve(333)

  },3000)

})

Promise.all(\[p1,p2,p3\]).then(res=> {

  console.log(res);

},error => {

  console.log(error);

})

//[
    //'hello',

    //222,

   // 333
//]

//222

注意,如果作为参数的 Promise 实例,自己定义了catch方法,那么它一旦被rejected,并不会触发Promise.all()的catch方法。

那如果自己定义了then呢?

const p1 = new Promise((resolve, reject) => {

  resolve('hello');})

.then(result => result)

.catch(e => e);


const p2 = new Promise((resolve, reject) => {

  throw new Error('报错了');})

.then(result => result)

.catch(e => e);


Promise.all([p1, p2])

.then(result => console.log(result))

.catch(e => console.log(e));

// ["hello", Error: 报错了]

上面代码中,p1会resolved,p2首先会rejected,但是p2有自己的catch方法,该方法返回的是一个新的 Promise 实例,p2指向的实际上是这个实例。该实例执行完catch方法后,也会变成resolved,导致Promise.all()方法参数里面的两个实例都会resolved,因此会调用then方法指定的回调函数,而不会调用catch方法指定的回调函数。

九、Promise.any()  一个成功就成功,都失败才失败   

目前还是提案中

npm install p-any
let p2 = new Promise((resolve, reject) => {

  reject(222)
  
})

let p3 = new Promise((resolve, reject) => {

  reject(333)
  
})

py([p2,p3]).then(res=> {

  console.log(res);
  
},error => {

  console.dir(error);
})

any.png

十、Promise.allSettled()  都结束才结束

如果参数不是Promise对象会先变成Promise对象  通过Promise.reslove()方法

let p1 = new Promise((resolve, reject) => {

  resolve(111)

})

let p2 = new Promise((resolve, reject) => {

  reject(222)

})

let p3 = new Promise((resolve, reject) => {

  reject(333)

}

Promise.allSettled([p1,p2,p3]).then(res => {

  console.log(res);

})

allSettled.png
返回的是数组,reslove的是status和value,  失败的是status和reason

res.filter(ites=>ites.status==='fulfilled') 

[
    {
        status: 'fulfilled',
        value: 111
    }
]

应用场景 : 不关心异步操作的结果,只关心这些操作有没有结束。例如loading结束

十一、Promise.race()  一个变就会变

const p = Promise.race([p1, p2, p3]);

如果参数不是Promise对象会先变成Promise对象  通过Promise.reslove()方法

那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。

let p1 = 'hello'

let p2 = new Promise((resolve, reject) => {

  setTimeout(function () {

    resolve(222)

  },3100)

})

let p3 = new Promise((resolve, reject) => {

  setTimeout(function () {

    reject(333)

  },3000)

})

Promise.race(\[p1,p2,p3\]).then(res=> {

  console.log(res);

},error => {

  console.log(error);

})

应用场景: timeout

const p = Promise.race([

  axios('/api'),

  new Promise(function (resolve, reject) {

    setTimeout(() => reject(new Error('request ****timeout')), 5000)

  })]);
十二、Promise.try()  

不知道或者不想区分,函数f是同步函数还是异步操作,但是想用 Promise 来处理它。因为这样就可以不管f是否包含异步操作,都用then方法指定下一步流程,用catch方法处理f抛出的错误。

不必理会函数本身是同步的还是异步的,

let p4 =() => new Promise((resolve, reject) => {

  reject(222)

})

let aa = () => {

  console.log(111111)

}

pt(p4).then(res=> {

  console.log(res);

}).catch(error=> {

  console.log(error);

})

console.log('test')

  
// test

// 222
十三、 应用

图片加载
  function loadImageAsync(url) {
    return new Promise(function(resolve, reject) {
      const image = new Image();
      image.onload = function() {
        resolve(image);
      };

      image.onerror = function() {
        reject(new Error('Could not load image at ' + url));
      };
      image.src = url;
      image.qu
    });
  }
  loadImageAsync('https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1587961648939&di=40166f91316c3f23c2964ee73ee72946&imgtype=0&src=http%3A%2F%2Fimg.kutoo8.com%2Fupload%2Fimage%2F95351812%2F001%2520%283%29_960x540.jpg')
  .then(img => {
    console.log(img);
    document.querySelector('body').appendChild(img)
  })

最后 说一下前边提到的问题,我现在的是用的常规的方法,就是一个请求完在请求第二个, 当第一个失败了,我就不去请求了,如果有更好的办法,请留言,在这里抱拳了


剧终
谢幕
阅读 115

推荐阅读
目录