Promise和async/await(一)

为什么会有同步和异步?

首先JS 是单线程的,单线程程序在执行的时候,所有的程序都是按照顺序执行的,前面的必须处理好后面的才会执行。
JS 是单线程的,但是浏览器加载一些需要网络请求的资源,ajax或者执行一段 setTimeout代码,由于是单线程,要等这些内容访问或者执行完才执行下面的代码,那么你发送ajax请求,执行setTimeout 的这段时间什么也做不了,这种效果对程序是一种堵塞(同步堵塞),这个时候异步就出现了,在涉及需要等待的操作,我们把代码交给其他对应的浏览器线程去执行,在执行结束的时候,通知我们的主线程执行完毕,你可以操作资源了,这段等待时间并不影响你程序的执行,只是在未来的某个时间段(不确定),有一个操作一定执行,这就是异步(异步非阻塞)

回调

异步和同步相比,最难掌控的就是异步任务会什么时候完成和完成之后的回调问题。
回调是异步编程最基本的方法
像下面的例子

listen( "click", function handler(evt){
    setTimeout( function request(){
        ajax( "http://some.url.1", function response(text){
            if (text == "hello") {
                handler();
            }
            else if (text == "world") {
                request();
            }
        } );
    }, 500) ;
} );
console.log('doSomething')

这种地域式的回调,令代码的可读性非常差!!

信任问题

在你不知道的javascript一书中,对于回调的信任问题做了阐述 当你使用第三方的库的方法处理回调时很有可能遇到以下信任内容
· 调用回调的过早
· 调用回调过晚
· 调用回调次数过多会过少
· 没有把所需要的环境/参数成功的传给你的回调函数
· 吞掉可能出现的错误或异常
· .......
那么怎么解决这种信任问题呢?

你需要一个承诺

当你把一件事情交给别人去做的时候,这个任务可能马上完成也可能一段时间后完成,这个人在任务完成或者失败的时候回给你一个回应,放心的人!!回应就表示成功了或者失败了,没回应就表示正在执行~~~

Promise(承诺) 就是这样人

【官方】Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象
三种状态:
1.pending(进行中)
2.fulfilled (已成功)通常也称为 resolved
3.rejected (已失败)
特点
1.对象状态不受外界影响。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态
2.一旦改变状态,就不会在改变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected
缺点
1.无法取消Promise,一旦新建它就会立即执行,无法中途取消。
2.如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。
3.当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

我们看一个简单的promise例子

Promise构造函数接受一个函数,该函数有两个参数 resolve 和 reject,他们也是两个函数。
resolve函数的作用是:将Promise 的状态从“未完成”变成“成功”(即从“pending”变成“resolved”),在异步操作成功的时调用,并将一部操作的结果,作为参数传递出去。
reject函数的作用是:将Promise 的状态从“未完成”变成“失败”(即从“pending”变成“rejected”),在异步操作成功的时调用,并将一部操作的结果,作为参数传递出去。
Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。

let promise = new Promise((resolve,reject)=>{
    // 接收一个callback。参数是成功函数与失败函数
    setTimeout(()=>{
       let num = parseInt(Math.random()*100);
       // 如果数字大于50就调用成功的函数,并且将状态变成Resolved
       if(num > 50){
          resolve(num);
       }else{
        // 否则就调用失败的函数,将状态变成Rejected
          reject(num)
       }
    },3000)
})

当Promise执行的内容符合你预期的成功条件的话,就调用resolve函数,失败就调用reject函数,这两个函数的参数会被promise捕捉到。可以在之后的回调中使用
创建一个承诺完成了,我们如何使用它呢

promise.then(res => {
  //在构造函数中如果你执行力resolve函数就会到这一步
  console.log(res)
}, err => {
  // 执行了reject函数会到这一步
  console.log(err);
})


Promise.prototype.then()

then方法接収两个函数,第一个是承诺成功(状态为resolved)的回调函数,第二个(可选)是承诺失败(状态为rejected)的回调函数。
then方法的返回值不是一个promise对象就会被包装成一个promise对象,所以then方法支持链式调用。
then方法的可以帮我们串行的解决一些逻辑问题,让我们的书写更加顺畅。
看看下面这个

ajax('first');
ajax('second');
ajax('third');
需要按顺序来执行怎么办?
ajax('first').success(function(res){
    ajax('second').success(function(res){
        ajax('third').success(function(res){
            //串行完毕可以执行你想要的内容了
        });
    })
})

上面地狱式的回调,可怕!!如果使用下面的 then 链式调用,就会好很多

let promise = new Promise((resolve,reject)=>{
    ajax('first').success(function(res){
        resolve(res);
    })
})
promise.then(res=>{
    return new Promise((resovle,reject)=>{
        ajax('second').success(function(res){
            resolve(res)
        })
    })
}).then(res=>{
    return new Promise((resovle,reject)=>{
        ajax('second').success(function(res){
            resolve(res)
        })
    })
}).then(res=>{
    // 串行完毕你要做的xxx可以开始了
})

串行说完了,那并行的怎么办,当我们有多个异步事件,之间并没有先后顺序,只需要全部完成就可以开始工作。
这个时候我们可以使用 Promise.all

Promise.all()

Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。

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

上面代码中,Promise.all()方法接受一个数组作为参数,p1、p2、p3都是 Promise 实例,如果不是,就会先调用下面讲到的Promise.resolve方法,将参数转为 Promise 实例,再进一步处理。另外,Promise.all()方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例。
p的状态由p1、p2、p3决定,分成两种情况。
1.只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
2.只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。

var p1 = new Promise((resolve, reject) => {
    resolve('p1')
})
var p2 = new Promise((resolve, reject) => {
    setTimeout(()=> {
         resolve('p1')
    }, 1000)
})
var p3 = new Promise((resolve, reject) => {
    resolve('p3')
})
Promise.all([p1, p2, p3])
.then(val => {
    console.log('then', val)
})
.catch(err => {
    console.log('catch', err)
})
//   then (3) ["p1", "p1", "p3"]

var p1 = new Promise((resolve, reject) => {
    resolve('p1')
})
var p2 = new Promise((resolve, reject) => {
    setTimeout(()=> {
         resolve('p1')
    }, 1000)
})
var p3 = new Promise((resolve, reject) => {
    reject('p3')
})
Promise.all([p1, p2, p3])
.then(val => {
    console.log('then', val)
})
.catch(err => {
    console.log('catch', err)
})
//  catch p3

注意: 若实例自带了 .then() 则先执行实例的 then 在执行 all的then;若实例自带了 .catch 则先调用 实例的 catch 再执行 .then()。

var p1 = new Promise((resolve, reject) => {
    resolve('p1')
}).then(val => {
    console.log(555, val)
})
var p2 = new Promise((resolve, reject) => {
    setTimeout(()=> {
         resolve('p1')
    }, 1000)
})
var p3 = new Promise((resolve, reject) => {
    resolve('p3')
})
Promise.all([p1, p2, p3])
.then(val => {
    console.log('then', val)
})
.catch(err => {
    console.log('catch', err)
})

// 555 'p1'
// then [undefined, 'p1', 'p3']


Promise.race()

Promise.race()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。

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

上面代码中,只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。

Promise.prototype.catch()

Promise.prototype.catch()方法是.then(null, rejection)或.then(undefined, rejection)的别名,用于指定发生错误时的回调函数。

var p = new Promise((resolve, reject) => {
    //此处抛出异常,状态就会变成rejected,就会调用catch() 方法的回调函数,处理这个错误。
    throw new Error("测试错误test!!!")  
})
p.catch((err) => {
    console.log(err)    // Error: 测试错误test!!!
})

等同于

// 写法一
var p = new Promise((resolve, reject) => {
    throw new Error("测试错误test!!!")  
})
p.then(null, (err) => {
    console.log(err)
})
// 写法二
var p = new Promise((resolve, reject) => {
    reject(new Error("错误测试test!!!!"))
})
p.catch((err)=> {
    console.log(err)
})
// 写法三
var p = new Promise((resolve, reject) => {
    try {
       throw new Error("错误测试test!!")
    } catch(err) {
        reject(err)
    }
})
p.catch((err)=> {
    console.log(err)
})

Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获。
一般来说,不要在then方法里面定义 Reject 状态的回调函数(即then的第二个参数),总是使用catch方法比较好。

// 不建议使用
var p = new Promise((resolve, reject) => {
    throw new Error("test-error")
})
p.then(val=> {
    console.log(1212)
}, error => {
    console.log(666, error);
})

// 建议使用
var p = new Promise((resolve, reject) => {
    throw new Error("test-error")
})
p.then(val=> {
    console.log(1212)
}).catch(error=> {
    console.log("error", error)
})


Promise.prototype.finally()

不管Promise最后状态如何,都会执行 finally() 。
finally() 方法的回调函数不接受任何参数。这也就意味着finally中无法知道 Promise 的状态。这表明,finally里面的操作应该和状态无关,不依赖与Promise 的执行结果。
finally的本质是 then 方法的特例
finally 方法不一定是最后一环,后面还可以在跟 .then() ,返回的是一个Promise

var p = new Promise((resolve, reject)=> {
    throw new Error("test~~")
})
p.then((val)=> {
    console.log(11, val)
}).finally(()=> {
    console.log('finally')
}).catch((err)=> {
    console.log(err)
})
//  finally
//  Error: test~~

还有

Promise.allSettled()

Promise.allSettled()方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例。只有等到所有这些参数实例都返回结果,不管是fulfilled还是rejected,包装实例才会结束。

Promise.any()

ES2021 引入了Promise.any()方法。该方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例返回。只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfilled状态;如果所有参数实例都变成rejected状态,包装实例就会变成rejected状态。
Promise.any()跟Promise.race()方法很像,只有一点不同,就是不会因为某个 Promise 变成rejected状态而结束。

注意:
all(), race(), allSettled(), any() 都是 Promise 的方法;
then(), finally(), catch() 都是Promise实例的方法挂载在Promise.prototype上;

Promise和async/await(二)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,837评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,551评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,417评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,448评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,524评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,554评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,569评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,316评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,766评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,077评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,240评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,912评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,560评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,176评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,425评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,114评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,114评论 2 352