JavaScript - Promise 学习笔记二

Promise 是 ES6 推出的另一个用于异步函数回调的构造函数.

它的使用方式大致如下.

var p = new Promise(function(reslove,reject){
    if (!err) reslove(data)
    
    reject(err)
})

p.then((data)=>{
    console.log(data) // success
},(err)=>{
    console.log(err)
})


一个实际的例子

function promiseGet(url) {
    return new Promise(function(reslove,reject){
        let xhr = new XMLHttpRequest()
        xhr.onload = function(data) {
            resolve(json.parse(xhr.responseText))
        }
        xhr.onError = function() {
            reject(xhr.statusText)
        }
        xhr.open('GET',url,true)
        xhr.send()
    })

}

promiseGet('./data/users/1')
    .then(
    data=>{
        console.log(data)
        },
    err=>{
        console.log(err)
        })

Promise 基本用法说明

我们可以将 Promise 想象成一个容器,这个容器里装一个异步操作的行为.并根据这个异步操作的返回的结果,来告知 Promise 那种是成功状态,哪一种是失败状态.

new Promise(function(resovle,reject){
    $.get('./data/users/1',function(data){
        if (!data.err) reslove(data.info)
        reject(data.err)
    })
})
  • 容器里装的异步行为是 $.get()

  • $.get(,callback) 告知了 Promisedata.err不存在或者为undefined的时候,是成功状态.调用 reslove 函数,并传入 data.info

  • $.get(,callback) 告知了 Promisedata.err 存在的时候是失败状态,调用 reject,并传入错误信息.

到目前为止,Promise 接受了一个 函数 作为参数.
此参数函数里,有两个回调函数.
但这些都只是形参,除了函数内部的异步操作. Promise没有拿到其他任何实质性的数据.


then又是什么?

现在,Promise有了一个异步的操作,它知道需要去执行异步操作了.
我们写代码的时候是 new Promise(function(reslove,reject){....})

传递给 Promise 构造函数的参数函数的 reslovereject 的两个形参,在什么位置赋值呢?

毕竟 Promise 至少在其内部的某个位置,执行了这个 带有异步任务的函数了

// 伪代码

class Promise {
    constructor(fn) {
        super()
        this.fn = fn // 接受到了我们传递的构造函数参数
    }
    
    someMoment() {
        this.fn(reslove???,reject???)
    }
}


then方法出场

我们在创建 Promise 对象的时候,传递了一个 fn .
Promise 到目前为止起码有一个 异步函数 fn.

假设我不知道 fn的两个形参如何去传的.那么,如果我没有传递这两个参数,Promise 接收到了这个没有设置实参到形参的异步回调函数时,会执行异步任务吗?

let pget = new Promise(function (reslove, reject) {
      $.get('http://localhost:8000/users/1', function (data) {
        console.log(data)
        if (!data.err) reslove(data.info)
        reject(data.err)
      })
    })

即使没有设置reslove|reject,异步请求仍然会同步的发送出去

请求发出去了,也就说说,Promise 一旦创建,即使没有设置,或者说不知道如何设置 reslove & reject 异步请求仍会在创建 Promise 的时候,同步发出去.

但在传递的异步函数内部 没有设置 reslove & reject 实参时却没有报错?

难道是?

 // reslove & reject 我压根就没传
console.log(`reslove:${reslove}`)
console.log(`reject:${reject}`)

即使没有传递reslove|reject,Promise也会默认帮我们转换成function

即使,我并没有传递这两个状态对应的回调函数,在打印它们的时候,发现Promise帮我们自动传递了两个对应的function.

意义在哪呢?

对于一个函数参数,如果我们没有传递的话?

function fn(p1) {
      console.log(p1) //undefined
      // p1() // p1 is not function
      // p1.a // Cannot read property 'a' of undefined
    }
fn()

到目前为止有两个疑问:

  • 对于一个没有设置回调处理reslove or rejectPromise . 它直接把异步请求发出去有意义吗?

  • 为什么Promise会在我们没有设置 reslove & reject两个回调函数的时候,主动的去把它俩设置成 function? 意义在哪?

这两个疑问暂且放置一边.


Promise.prototype.then 1.0

thenPromise 对象的实例方法.
它用来指定 初始化异步任务函数的 reslove,reject 实参.

let pget = new Promise(function (reslove, reject) {
      $.get('http://localhost:8000/users/1', function (data) {
        console.log(data)
        // reslove & reject 我压根就没传
        console.log(`reslove:${reslove}`) // function {navie code}
        console.log(`reject:${reject}`) // function {navie code}
        if (!data) reslove(data)
        reject('出错啦!')
      })
    })    
p.then(data=>{
    console.log(data)
},err=>{
    console.log(err)
})

使用 then 方法,为每一个 Promise 对象设置 reslove & reject

我们使用then,把reslove & reject 设置成了 reject

也就是说:

  • 我们不设定 reslove & reject , Promise 会帮我们封装两个在我们看来什么都不做的 reslove & reject

  • 如果我们使用then设定了 reslove & reject , Promise 就使用我们设定的这两个回调函数.

  • 所以,不管什么情况, Promise 都会需要这两个函数.没有就自己创建,有就直接用.

Promise.prototype.then 2.0

到目前为止,我们知道了

  • 使用 new Promise 创建一个 Promise 对象

  • 构造函数里需要传递一个匿名函数参数.

    • 包括异步操作
    • reslove
    • reject
  • 使用 then 来执行当前这个Promise在异步操作状态发生改变时的 reslove & reject

Promisethen 是可以链式调用的.

then方法返回的是promise对象

因为 then 方法返回的仍然是 Promise 类型.

then方法返回的是原来的那个 Promise对象 还是一个新的 Promise对象呢?

var p1 = pCreate('step 01')
var p2 = p1.then(data => {
  console.log(data)
  return 1
})

console.log(p1 instanceof Promise) // true
console.log(p2 instanceof Promise) // true
console.log(p1 === p2) //false
每一次then都会一个promise对象,且每一次都是一个新的

结论:

+ `then` 方法在执行完毕之后,确实又创建了一个 `Promise`对象

+ 每次 `then` 创建的 `Promise` 对象是不相同的.

为什么每次then指向完毕之后都是返回的一个全新的Promise而不是原来的那个Promise?

一般的链式调用里面都是利用return this来实现的.

个人理解:

这是和Promise的特性相关的

  • Promise主要是做异步任务的.
  • 异步任务回来之后,就需要知道成功或者失败了.
  • 当我们使用then指定Promise成功或者失败的回调之后,这个Promise的使命就算完成了.
  • 异步任务不等于事件,比如按钮点击,可以重复执行.
  • 当知道成功和失败之后,这个异步任务就算做完了.
  • 所以,一个 then 就代表了老的 Promise 任务完结,新的 Promise准备就绪.

Promise.prototype.then 3.0

then的链式调用

function pCreate(stepName) {
  return new Promise(function (reslove, reject) {
    console.log('in')
    setTimeout(function () {
      reslove(stepName)
      reject(stepName)
    }, 100)
  })
}

pCreate(0) // 第一个 promise -- pCreate() 返回
  .then(data => { // 
    console.log(data)
    return 1
  })
  .then(data => {  // 第二个 promise 由上一个 then 返回
    console.log(data)
    return 2
  })
  .then(data => { // 第三个 promise 由上一个 then 返回
    console.log(data)
    return 3
  })
  .then(data => { // 第四个 promise 由上一个 then 返回
    console.log(data)
  })
  
一个then执行完,就会返回一个promise
  • 这段then链式调用,一共产生了4个Promise.

  • 除了第一个Promise我们手动了使用 pCreate() 方法调用(输出in)之外,剩下三个由then调用的,都是系统内置的 Promise创建方法.

  • 上一个 promise.then.reslove 返回的数据被下一个 promise.then.reslove 接收到了.

  • 也就说,如果在上一个 thenreslove 里有返回值,那么在这个then执行完毕并创建下一个 promise同时,也会把 return 的这个 data 设置成下一个 thenreslove 的第一个实参.

  • 这样做的好处,就是可以把一个 promise 最终在 reslove 里产生的数据传递到下一个 Promisereslove 中去.

一个then干了三件事情

  • 设置自己promisereslove & reject

  • 创建一个新的 promise

  • 把自己 promisereslove& reject 里的数据传递给新的 promisereslove & reject 中.

then既设置了promise的reslove&reject.同时又创建了一个新的promise.接着还可以把自己的数据传递给下一个promise的reslove&reject


Promise.prototype.then 4.0

既然then的箭头函数可以传递数据到下一个then的箭头函数里 那么,如果我在then箭头函数里返回一个新的 promise 呢?

问题就变得奇怪了.

因为then会创建一个promise,我又在它的箭头函数里返回一个新的 promise.

但不管怎么说,在代码级别,这样做肯定是可以的.

毕竟then里无非是两个箭头函数,表示 reslove,reject.

函数里,我想返回什么就返回什么.

function pCreate(stepName) {
  return new Promise(function (reslove, reject) {
    setTimeout(function () {
      reslove(stepName)
      reject(stepName)
    }, 1000)
  })
}


pCreate('step 01')
  .then(data => {
    console.log(`data:${data}`)
    return pCreate('step 02')
  })
  .then(data2 => {
    console.log(data2) // step 02
    console.log(data2 instance of 
  })

按照上一阶段的理解,上一个then.reslove里返回了 return 1,那么下一个then.reslove(data) === 1.

所以,这里的 data2 应该是 promise 对象才对.

但结果如下:

data2并不是上一个then.reslove的返回值promise
  • 当上一个 then.reslove 里返回的是一个非 promise 对象时,下一个 then.reslove(data) 就等于上一个 reslove里的返回值

  • 当上一个 then.reslove 里返回的一个 promise对象是(return pCreate('step 02')). 下一个 then.reslove(data) 就不是一个 promise 对象了.

但我们发现,console.log(data2) 输出的是 step 02.

就等于是,第二个通过 return pCreate('step 02')直接设置 then 了一样.

pCreate('step 02')
    .then(data=>{
        console.log(data) // step 02
    })

结论:

  • 当上一个then.reslove 中返回的是一个 promise对象时.

  • 这个 promise 对象直接就用做成下一个 then 的函数调用者

  • 所以,下一个then.reslove(**data**) 就是上一个 promise 成功时调用 reslove(**data**) 传送的 data.

  • 所以,在我们使用 console.log(data2)时 ,data2 instanceof Promise:false & data2:step 02


Promise.prototype.then 5.0

既然可以在 then.reslove里继续返回 promise.
那么,我们可以把一些有依耐性的接口使用 then 串联起来.

 function pGet(url) {
      return new Promise(function (reslove, reject) {
        get(url, function (data) {
          reslove(data)
        })
      })
    }

    function get(url, callback) {
      let xhr = new XMLHttpRequest()
      xhr.open('GET', url, true)
      xhr.send()
      xhr.onload = function () {
        callback && typeof callback === 'function' && callback(JSON.parse(xhr.responseText))
      }
    }

    pGet('http://localhost:8000/users/1')
      .then(function(data){
        console.log(data)
        let userInfo = data
        return pGet(`http://localhost:8000/preferences/${userInfo.preferences}`)
      })
      .then((data)=>{
        console.log(data)
        let preferences = data
        return pGet(`http://localhost:8000/hobbies/${preferences.hobbies}`)
      })
      .then(data=>{
        console.log(data)
        let hobbies = data
        var hobbiesStr = hobbies && hobbies.values instanceof Array && hobbies.values.join(',')
        document.querySelector('h1').innerText = hobbiesStr
      })
使用then,返回promise链式的有顺序的调用接口获取异步数据
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,186评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,858评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,620评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,888评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,009评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,149评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,204评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,956评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,385评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,698评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,863评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,544评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,185评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,899评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,141评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,684评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,750评论 2 351

推荐阅读更多精彩内容

  • Promise 对象 Promise 的含义 Promise 是异步编程的一种解决方案,比传统的解决方案——回调函...
    neromous阅读 8,703评论 1 56
  • Promiese 简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果,语法上说,Pr...
    雨飞飞雨阅读 3,352评论 0 19
  • 一、Promise的含义 Promise在JavaScript语言中早有实现,ES6将其写进了语言标准,统一了用法...
    Alex灌汤猫阅读 820评论 0 2
  • Promise含义 Promise是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更强大。所谓Pr...
    oWSQo阅读 1,084评论 0 4
  • 【如果那些故事真的发生过,那么我们是谁?】 这几日闹得沸沸扬扬的“首例免疫艾滋病基因编辑婴儿”事件,让我有一种很诡...
    蔚来漫谈阅读 578评论 3 3