资讯专栏INFORMATION COLUMN

promise学习(1)

pingink / 767人阅读

摘要:原文地址定义是针对异步编程的一种解决方案,能够抽象处理异步对象以及对其进行操作。当状态由时执行已完成的回调函数,由时,调用失败的回调函数。所以,可以在自行使用,保证执行不会出错,或者使用其他异步编程方式。异步编程的方案后续学习再补充

原文地址

定义

Promise是针对异步编程的一种解决方案,能够抽象处理异步对象以及对其进行操作。Promise并不是Javascript语言的扩展,只是在ES6中将其写进了语言标准,原生Javascript提供了Promise对象,统一了用法。

功能

可以在一定程度上优化回调地狱问题,当需要控制多个异步操作时,代码结构更加清晰,便于理解。

提供统一的API,书写更加规范,便于维护,便于控制异步操作。

原理

一个 Promise 对象代表一个目前还不可用,但是在未来的某个时间点可以被解析的值。

可以理解为Promise能够以同步的方式编写异步代码,但是有更优的解决方式。

Promise的状态

Promise对象存在三种状态:pending、fulfilled、rejected。其中pending 状态最终一定会变为fulfilled或者是rejected中的一种,且不会再发生改变。整个过程由Promise机制保证不会受到外界干扰。当状态由pending->fulfilled时执行已完成的回调函数,由pending->rejected时,调用失败的回调函数。一旦状态改变,就不会再被修改而一直保持这个状态。

基本API

实例方法

Promise#then

Promise#catch

静态方法

Promise.resolve

Promise.reject

Promise.all

Promise.race

问题&思考 什么是同步,什么是异步?

大家常说Javascript是单线程的语言,这固然是对的。这里的单线程是说,在JS引擎中负责解释和执行JavaScript代码的线程只有一个,每个特定的时刻只有特定的代码被执行,并阻塞其他代码。换句话说,JavaScript是单线程,但是浏览器是多线程的。除了负责解析JS代码之外,还存在其他线程,负责HTTP请求,DOM处理等等。

同步可以理解成在一个函数返回时,我就拿到了需要的结果,无需等待。异步则是函数返回时,我没有得到应该得到的结果,而且要在未来某一时间才能得到。程序执行的顺序和任务执行的顺序并不始终保持一致。

前端的哪些操作需要使用异步编程?

常见的操作有接口请求,文件读取,定时器操作等。在浏览器端耗时很长的操作,最好都异步执行。(鼠标点击事件、窗口大小拖拉事件等也可以算是异步的)

Promise概念和使用上有哪些需要注意的地方?
关于Promise的状态

promise对象的状态,只有对象处于pending状态时是可变的,一旦从Pending转换为FulfilledRejected之后, 这个promise对象的状态就不会再发生任何变化。也就是说,Promise与Event等不同,在.then 后执行的函数可以肯定地说只会被调用一次,而且只会执行fulfilled或者rejected中的一个。

创建promise对象

一般情况下会使用new Promise创建对象,除此之外,也可以使用其他方法。

静态方法Promise.resolve(value) 可以认为是new Promise()方法的快捷方式,同理Promise.reject(error)也是。

thenable对象&promise对象

thenable对象和promise对象并不是一回事,thenable对象可以理解为具有.then方法的对象。

可以通过Promise.resolve将thenable对象转化为promise对象。

Promise链式调用

promise的链式调用可以通过使用.then处理回调函数比较多的情况,一定程度上解决回调地狱的问题。链式调用的原理(后续可以多带带介绍下)简单来说是Promise内部有一个defers队列存放事件,程序执行时.then将下个事件放入,promise状态变化结束之后出发响应函数执行,然后将事件从队列中移除。因为每一次调用.then时都会创建一个全新的promise对象,所以保证了链式调用可以顺利执行。

链式调用的参数传递顺序

demo1

前一个task的返回值作为后一个task的参数。不使用链式调用,直接调用.then方法时,由于每次都是新建的promise对象,所以value的值始终为100。

// 1: 对同一个promise对象同时调用 then 方法
var aPromise = new Promise(function (resolve) {
    resolve(100);
});
aPromise.then(function (value) {
    return value * 2;
});
aPromise.then(function (value) {
    return value * 2;
});
aPromise.then(function (value) {
    console.log("1: " + value); // => 100
})
// vs
// 2: 对 then 进行 promise chain 方式进行调用
var bPromise = new Promise(function (resolve) {
    resolve(100);
});
bPromise.then(function (value) {
    return value * 2;
}).then(function (value) {
    return value * 2;
}).then(function (value) {
    console.log("2: " + value); // => 100 * 2 * 2
});
链式调用多个task的执行顺序

在链式调用过程中,每一个.then调用时候都会创建一个全新的promise对象,.then调用结束时会执行对应状态的回调函数,

但是如果没有传入rejected的回调,或者.catch使用的位置不同,都会导致最后的结果不同。

demo2

task全部正常执行:

function taskA() {
    console.log("Task A");
}
function taskB() {
    console.log("Task B");
}
function onRejected(error) {
    console.log("Catch Error: A or B", error);
}
function finalTask() {
    console.log("Final Task");
}
var promise = Promise.resolve();
promise
    .then(taskA)
    .then(taskB)
    .catch(onRejected)
    .then(finalTask);

输出结果为:

Task A
Task B
Final Task

当taskA执行失败时:

function taskA() {
    console.log("Task A");
    throw new Error("throw Error @ Task A")
}

输出结果为:

Task A
Error: throw Error @ Task A
Final Task

由于taskB中并没有注册rejected函数,所以taskB并未执行,而是直接通过catch捕获到了失败。

如果在taskB的then中传入了失败的回调:

promise
    .then(taskA)
    .then(taskB,function(error) {
        console.log("b-catch-error")
    })
    .catch(onRejected)
    .then(finalTask);

执行结果为:

Task A
b-catch-error
Final Task

所以,在多个.then链式调用时,不同位置报错会导致task执行顺序不同,执行不同,也许不符合预期。

所以,可以在自行使用try-catch,保证.then执行不会出错,或者使用其他异步编程方式。

每一次调用.then,都会返回新的Promise对象

demo3

下面代码中每一次返回的promise对象都不是同一个对象。

var aPromise = new Promise(function (resolve) {
    resolve(100);
});
var thenPromise = aPromise.then(function (value) {
    console.log(value);
});
var catchPromise = thenPromise.catch(function (error) {
    console.error(error);
});
console.log(aPromise !== thenPromise); // => true
console.log(thenPromise !== catchPromise);// => true

理解了这个再来看下面的问题。

demo4 错误的调用

function badAsyncCall() {
    var promise = Promise.resolve();
    promise.then(function() {
        // 任意处理
        return 1;
    });
    return promise;
}

demo5 正确的调用

function anAsyncCall() {
    var promise = Promise.resolve();
    return promise.then(function() {
        // 任意处理
        return 1;
    });
}

返回结果

demo4中return 的promise并不是执行过.then的promise,所以并没有返回预期的结果。在实际开发中要注意这个问题。

rejected并不一定会捕获到异常

但看这个字面上可能会有些疑惑,实际上还是概念上的问题。

使用promise.then(Fulfilled, Rejected)的话,在Fulfilled 中发生异常的话,在Rejected中是捕获不到这个异常的。原因是promise状态只能从pending变成fulfilled或者是rejected,且变化完成之后不会再被修改。所以一旦执行了fulfilled,说明promise成功了,此时就已经不会再执行rejected了。

promise 不要在异步和同步混合使用

demo6

function onReady(fn) {
    var readyState = document.readyState;
    if (readyState === "interactive" || readyState === "complete") {
        fn();
    } else {
        window.addEventListener("DOMContentLoaded", fn);
    }
}
onReady(function () {
    console.log("DOM fully loaded and parsed");
});
console.log("==Starting==");

如果这段代码在源文件中出现的位置不同,在控制台上打印的log消息顺序也会不同。 结果不可控这是极力要避免的。

正确的写法应该是:

function onReadyPromise() {
    return new Promise(function (resolve, reject) {
        var readyState = document.readyState;
        if (readyState === "interactive" || readyState === "complete") {
            resolve();
        } else {
            window.addEventListener("DOMContentLoaded", resolve);
        }
    });
}
onReadyPromise().then(function () {
    console.log("DOM fully loaded and parsed");
});
console.log("==Starting==");

为了避免上述中同时使用同步、异步调用可能引起的混乱问题,Promise在规范上规定 Promise只能使用异步调用方式

异步编程的方案

(后续学习再补充)

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

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

相关文章

  • 深入学习Promise调用链

    摘要:前言使用中,链式的调用对于控制异步执行很重要。的链式调用是支持链式调用的,但是它是不同于上面的链式。是调用方法返回自身,但是是调用方法后返回一个新的。的运行机制请参考的运行机制值穿透由于通过没有成功添加回调函数,发生了值穿透。 前言 使用Promise中,链式的调用对于控制异步执行很重要。 链式调用 在jQuery的使用中,我们常常使用下面的代码 $(#app).show().css(...

    tianren124 评论0 收藏0
  • promise学习(2)

    摘要:所谓的能对状态进行操作的特权方法,指的就是能对对象的状态进行等调用的方法,而通常的的话只能在通过构造函数传递的方法之内对对象的状态进行操作。一般会在构造函数中编写逻辑,什么时候执行回调,什么时候执行回调。 原文地址 1. 在then中使用reject 如果一个promise最初只定义了resolve,但是还想要使用reject怎么办? 可以在then中返回一个新的promise。这个...

    firim 评论0 收藏0
  • Promise源码学习(2)

    摘要:源码学习本篇为上一篇源码学习的补充,主要是来介绍和方法。那个率先改变的实例的返回值,就传递给的回调函数。基本介绍可见阮一峰老师的书籍。的状态由决定,分成两种情况。只有的状态都变成,的状态才会变成,此时的返回值组成一个数组,传递给的回调函数。 Promise源码学习(2) 本篇为上一篇源码学习(1)的补充,主要是来介绍Promise.all()和Promise.race()方法。闲话少叙...

    cfanr 评论0 收藏0
  • 嗨,了解一下,我的Promise学习笔记

    摘要:回调函数成功回调处理器失败回调处理器用户发送一个向百度服务器获取数据的异步请求无阻塞高并发的的诞生更加严重的依赖异步操作才能完成无阻赛高并发的特性。 Promise Promise 是什么? 词语本意: 发音:[ˈprɒmɪs] 词性:名词, 翻译:许诺,允诺。 MDN解释 Promise 对象用于一个异步操作。 一个Promise表示一个现在,将来或永不可能可用的值。 按照书写方...

    yanest 评论0 收藏0
  • JavaScript基础——深入学习async/await

    摘要:等待的基本语法该关键字的的意思就是让编译器等待并返回结果。这里并不会占用资源,因为引擎可以同时执行其他任务其他脚本或处理事件。接下来,我们写一个火箭发射场景的小例子不是真的发射火箭 本文由云+社区发表 本篇文章,小编将和大家一起学习异步编程的未来——async/await,它会打破你对上篇文章Promise的认知,竟然异步代码还能这么写! 但是别太得意,你需要深入理解Promise后,...

    张金宝 评论0 收藏0
  • promise学习(3)

    摘要:此时,由于只有一个的状态能够确定,所以执行的是唯一那个确定状态的函数,而不会执行其他的,但是并不会阻止其他的执行。在实际应用中,常用来设置超时操作,比如接口请求超时等。思考这个其实并不是矛盾,接受的是返回的的状态,与原来的没有关系。 原文地址 Promise.race // `delay`毫秒后执行resolve function timerPromisefy(delay) { ...

    阿罗 评论0 收藏0

发表评论

0条评论

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