资讯专栏INFORMATION COLUMN

Promise之深入浅出

littleGrow / 1737人阅读

摘要:不兼容问题,本文不予以处理,出门左转,找谷哥。如果中的回调函数抛出一个错误,那么返回的将会成为拒绝状态,并且将抛出的错误作为拒绝状态的回调函数的参数值。

Promise与async

主要内容:

promise基本实现原理

promise 使用中难点(链式调用,API基本上返回都是一个新Promise,及参数传递)

promise 对异常处理

参考:

​ 30分钟,让你彻底明白Promise原理

​ 阮一峰ES6入门

​ JavaScript Promise:简介

0. 基本用法

基本的promise使用

1. 兼容性

查看caniuse

查兼容性 基本上 主流浏览器支持没有问题。

IE不兼容 问题,本文不予以处理,出门左转,找谷哥。具体查看 babel,或者 自己实现一个Promise

2. ajax XMLHttpRequest封装
//get 请求封装
function get(url) {
  // Return a new promise.
  return new Promise(function(resolve, reject) {
    // Do the usual XHR stuff
    var req = new XMLHttpRequest();
    req.open("GET", url);

    req.onload = function() {
      // This is called even on 404 etc
      // so check the status
      if (req.status == 200) {
        // Resolve the promise with the response text
        resolve(req.response);
      }
      else {
        // Otherwise reject with the status text
        // which will hopefully be a meaningful error
        reject(Error(req.statusText));
      }
    };

    // Handle network errors
    req.onerror = function() {
      reject(Error("Network Error"));
    };

    // Make the request
    req.send();
  });
}
1. Promse API

Promise API 分为 :MDN

静态方法

prototype上方法

Promise.prototype.then() 来分析

首先来看看 Promise.prototype.then()返回一个Promise,但Promise内部有返回值,且 返回值,可以是个值,也可能就是一个新Promise

   *具体规则如下:*

- *如果then中的回调函数返回一个值,那么then返回的Promise将会成为接受状态,并且将返回的值作为接受状态的回调函数的参数值。*
- *如果then中的回调函数抛出一个错误,那么then返回的Promise将会成为拒绝状态,并且将抛出的错误作为拒绝状态的回调函数的参数值。*
- *如果then中的回调函数返回一个已经是接受状态的Promise,那么then返回的Promise也会成为接受状态,并且将那个Promise的接受状态的回调函数的参数值作为该被返回的Promise的接受状态回调函数的参数值。*
- *如果then中的回调函数返回一个已经是拒绝状态的Promise,那么then返回的Promise也会成为拒绝状态,并且将那个Promise的拒绝状态的回调函数的参数值作为该被返回的Promise的拒绝状态回调函数的参数值。*
- *如果then中的回调函数返回一个未定状态(pending)的Promise,那么then返回Promise的状态也是未定的,并且它的终态与那个Promise的终态相同;同时,它变为终态时调用的回调函数参数与那个Promise变为终态时的回调函数的参数是相同的。*

上面是官方规则,神马,具体白话就是 核心是 返回参数及返回promise的状态

参考:MDN

是不是 觉得很晕,没关系,可以先看 下一节,看完后,再回过来看具体的说明
2. Prmise 链式调用

链式调用

 1.  核心就是 then catch 等方法返回一个Promise
2.  链式 调用数据传递(注意)
1. 值传递问题

简单例子

//正常状态
const promise1 = new Promise((resolve, reject) => {
    resolve("0000")//
})
promise1.then(result => {
    console.log(result) //0000
       return "1111";//类似于 return Promise.resolve("1111"); 参数是data,promise 状态时 resolve
}).then(data => {
    console.log(data) // 1111
})

一个实际的例子:(拿来大神的例子JavaScript Promise:简介)

get("story.json").then(function(response) {
  console.log("Success!", response);
})
//这里的 response 是 JSON,但是我们当前收到的是其纯文本。也可以设置XMLHttpRequest.responseType =json
get("story.json").then(function(response) {
  return JSON.parse(response);
}).then(function(response) {
  console.log("Yey JSON!", response);
})
//由于 JSON.parse() 采用单一参数并返回改变的值,因此我们可以将其简化为:
get("story.json").then(JSON.parse).then(function(response) {
  console.log("Yey JSON!", response);
})
function getJSON(url) {
  return get(url).then(JSON.parse);
}
//getJSON() 仍返回一个 promise,该 promise 获取 URL 后将 response 解析为 JSON。
2. 异步操作队列

上面至今是return 值 ,直接调用 下一下then就OK了。

但如果return Promise,则?

Promise.resolve(111).then(function(d){
    console.log(d);
    return Promise.resolve(d+111);//返回promise
}).then(function(d2){
    console.log(d2);
})
// 111,222
3. 基本实现原理

主要是 如何自己实现一个简单的Promise

//极简实现
function Promise(fn) {
    var value = null,
        callbacks = [];  //callbacks为数组,因为可能同时有很多个回调

    this.then = function (onFulfilled) {
        callbacks.push(onFulfilled);
    };

    function resolve(value) {
        callbacks.forEach(function (callback) {
            callback(value);
        });
    }

    fn(resolve);
}
4. finnaly 实现
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 })
  );
};
5. 异常处理

异常分类:

同步异常

异步异常 无法try-catch 得到

多层Promise嵌套,获异常取具体的一个promise异常,而不是全部

1. Promise 异常处理基本套路

基本处理异常中,有两种方案then(undefined, func)catch()

then(undefined, func)catch()不同,具体参见代码方案3

//方案1 使用 Promise.prototype.catch()来catch
const promise1 = new Promise((resolve, reject) => {
    reject("no")// 
})
promise1.then(result => {
    console.log(result) // 永远不会执行
}).catch(error => {
    console.log(error) // no
})
//方案2 使用 Promise.prototype.then()中第二个参数 来处理
const promise1 = new Promise((resolve, reject) => {
    reject("no")// 
})
promise1.then(result => {
    console.log(result) // 永远不会执行
},error => {
    console.log(error) // no
})
//方案2  (方案1  方案2 对比)
var promise2 = new Promise((resolve, reject) => {
    resolve("yes")// 
})
promise2.then(result => {
    throw new Error("then");
    console.log(result) 
},error => {
    console.log("1111",error) // no
}).catch(error=>{
   console.log("2222",error)// 最终 err在此处被捕获,而不是 then 中
})
2. 异常不同分类

Promise可能遇到的异常种类

//1.异常 reject()
const promise1 = new Promise((resolve, reject) => {
    reject("no")// 
})
promise1.then(result => {
    console.log(result) // 永远不会执行
}).catch(error => {
    console.log(error) // no
})
//2.异常 显示throw
const promise1 = new Promise((resolve, reject) => {
    throw Error("no")
})
promise1.then(result => {
    console.log(result) // 永远不会执行
}).catch(error => {
    console.log(error) // 
})
//3.执行异常
const promise1 = new Promise((resolve, reject) => {
    aaaa;
})
promise1.then(result => {
    console.log(result) // 永远不会执行
}).catch(error => {
    console.log(error) // 
})
3. 异常链式调用
asyncThing1().then(function() {
  return asyncThing2();
}).then(function() {
  return asyncThing3();
}).catch(function(err) {
  return asyncRecovery1();
}).then(function() {
  return asyncThing4();
}, function(err) {
  return asyncRecovery2();
}).catch(function(err) {
  console.log("Don"t worry about it");
}).then(function() {
  console.log("All done!");
})

上述代码的流程图形式:

流程图形式

// promise链式调用,catch住异常后,后面就不会处理异常了
Promise.reject().then(()=>{
  console.log(2222);
},(err)=>{
    console.log(333,err)
    return err})
.catch((err)=>{
  console.log(1111,err);
})
//333 undefined  ,没有打印 1111
//如果 在链式调用中,then 第二个参数 catch住了异常,没有return Promise.reject()则后续链式调用返回rosolve状态pormise
Promise.reject()
   .then(()=>{
      console.log(111);
    },(err)=>{
        console.log(111,err) //reject 
        return err;
    }).then((data)=>{
        console.log(222,data);//resolve 执行
    },(err)=>{
      console.log(222,err); //未执行
    })
//4444 没有执行 1111
4. 异常丢失

很多情况下,promise无法捕获异常

场景1 macrotask 队列中抛出异常:

//场景1
//永远不要在 macrotask 队列中抛出异常,因为 macrotask 队列脱离了运行上下文环境,异常无法被当前作用域捕获。
function fetch(callback) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
             throw Error("用户不存在")
        })
    })
}

fetch().then(result => {
    console.log("请求处理", result) // 永远不会执行
}).catch(error => {
    console.log("请求处理异常", error) // 永远不会执行
})

// 程序崩溃
// Uncaught Error: 用户不存在

/*
    参考
    作者:黄子毅
    链接:https://www.jianshu.com/p/78dfb38ac3d7
    來源:简书
    简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
*/
//解决场景1 怎么解决,因为setTimeout 是macrotask任务,执行上下文完全不同
/**
    如何解决?
    调用reject
*/
function fetch() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            reject("收敛一些")
        })
    })
}
fetch().then((resolve, reject) => {
    console.log("resolve");
}).catch(error => {
    console.log("捕获异常", error) // 捕获异常 收敛一些
})

场景二 Promise 状态只能改变一次

 //异常丢失
   const promise2 = new Promise((resolve, reject) => {
       reject("no")
       console.log("reject after")
     throw Error("no") //异常丢失
   })
   promise1.then(result => {
       console.log(result) // 永远不会执行
   }).catch(error => {
       console.log("err",error) // no
   }).catch(error => {
       console.log("err2",error) // 也无法捕获异常
   })
6.async

基本语法

function timeout(ms) {
  return new Promise((resolve) => {
    setTimeout(resolve, ms);
  });
}

async function asyncPrint(value, ms) {
  await timeout(ms);
  console.log(value);
  return value;   //类似 return Promise.resolve(value)
}
//async 返回一个promise
asyncPrint("hello world", 50).then(function(d){
   console.log("then",d);
});
/** 打印
hello world
then hello world
*/

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/98156.html

相关文章

  • es6深入理解promise

    摘要:形式非必须,也非必须调用把用函数表示在调用的时候用函数代码更加同步化三是什么异步操作的终极解决方案写法四总结不管用还是用还是用,都保证你写的的返回值是一个对象 一、promise入门 1. Promise对象是什么 回调函数的另一种原生实现,比之前回调函数的写法机构清晰,功能强大, 2.以前回调这么写 function a(fn){ let h = 1; setTime...

    luckyw 评论0 收藏0
  • 深入探析koa异步回调处理篇

    摘要:而之后,我们得到的是一个是一个对象,我们可以使用语句定义回调函数,函数的内容呢,则是将读取到的返回给并继续让从断点处执行。 在上一篇中我们梳理了koa当中中间件的洋葱模型执行原理,并实现了一个可以让洋葱模型自动跑起来的流程管理函数。这一篇,我们再来研究一下koa当中异步回调同步化写法的原理,同样的,我们也会实现一个管理函数,是的我们能够通过同步化的写法来写异步回调函数。 1. 回调金字...

    Drinkey 评论0 收藏0
  • ES6-7

    摘要:的翻译文档由的维护很多人说,阮老师已经有一本关于的书了入门,觉得看看这本书就足够了。前端的异步解决方案之和异步编程模式在前端开发过程中,显得越来越重要。为了让编程更美好,我们就需要引入来降低异步编程的复杂性。 JavaScript Promise 迷你书(中文版) 超详细介绍promise的gitbook,看完再不会promise...... 本书的目的是以目前还在制定中的ECMASc...

    mudiyouyou 评论0 收藏0
  • 再谈Promise

    摘要:方法完成回调注册模式下,对象通过方法调用,注册完成态和失败态的回调函数。这些回调函数组成一个回调队列,处理的值。调用实例的方法,能使注册的回调队列中的回调函数依次执行。 之前写了一篇关于ES6原生Promise的文章。近期又读朴灵的《深入浅出Node》,里面介绍了一个Promise/Deferred模式。 Promise是解决异步问题的利器。它其实是一种模式。Promise有三种状态,...

    chenjiang3 评论0 收藏0
  • JavaScript 异步编程的四种方式

    摘要:异步编程是每个使用编程的人都会遇到的问题,无论是前端的请求,或是的各种异步。本文就来总结一下常见的四种处理异步编程的方法。利用一种链式调用的方法来组织异步代码,可以将原来以回调函数形式调用的代码改为链式调用。 异步编程是每个使用 JavaScript 编程的人都会遇到的问题,无论是前端的 ajax 请求,或是 node 的各种异步 API。本文就来总结一下常见的四种处理异步编程的方法。...

    microelec 评论0 收藏0

发表评论

0条评论

littleGrow

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<