JavaScript Promise介绍

JavaScript Promise介绍

前言

众所周知,在JavaScript的世界中,代码都是单线程执行的。由于这个原因,JavaScript中的耗时操作,如网络操作、浏览器事件等,都需要异步执行。这也导致在JavaScript中异步操作是非常频繁且常见的。

异步概念

在执行某些耗时、不会立即返回结果的操作时,不会阻塞后面的操作,一旦该耗时操作完成时,立即通知需要调用其结果的函数来做后续处理。

简单来理解就是:同步按照写的代码顺序执行,异步不按照代码顺序执行

回调函数进行异步操作

和同步操作不同,异步操作是不会立即返回结果的(如发起网络请求,下载文件,操作数据库等)。如果我们后续的函数需要之前返回的结果,又怎样使之前的异步操作在其完成时通知到后续函数来执行呢?

通常,我们可以将这个函数先定义,存储在内存中,将其当做参数传入之前的异步操作函数中,等异步操作结束,就会调用执行这个函数,这个函数就叫做回调函数(callback)。

举个栗子:

// 下载
function download(callback){
    // 模拟异步操作
    setTimeout(function(){
        // 调用回调函数
        callback('下载完成');
    }, 1000);
}

function callback(value){
    // 下载完成的处理
    console.log(value);
}

download(callback);

// 这段代码将在1秒后在控制台打印“下载完成”

但假如callback函数同样是个异步函数,且callback里又嵌入了callback呢? 例如需求是等待第一个文件下载完成后,再下载第二个文件,等待第二个文件下载完成后,再下载第三个文件...,这样的话,上面这种方法就不可取了,因为会产生很多的函数嵌套,嵌套太深容易引发回调地狱

Promise进行异步操作

古人云:“君子一诺千金”,这种“承诺将来会执行”的对象,在JavaScript中称为Promise对象。

我们首先看看如何通过Promise改造上面的回调函数异步操作

var download = new Promise(function(resolve, reject) {
    // 模拟异步操作
    setTimeout(function(){
        resolve('下载完成');
    }, 1000);
})

download.then(value => {
    console.log(value);
});

// 这段代码将在1秒后在控制台打印“下载完成”

可以看到代码简洁了一些,当然,不只是如此,它还可以连续调用,例如我们上面所说的回调地狱问题,通过Promise可以很简洁的实现

var download1 = new Promise(function(resolve, reject) {
    // 模拟异步操作
    setTimeout(function(){
        console.log('1');
        resolve('文件1下载完成');
    }, 1000);
});

var download2 = new Promise(function(resolve, reject) {
    // 模拟异步操作
    setTimeout(function(){
        console.log('2');
        resolve('文件2下载完成');
    }, 1000);
});

var download3 = new Promise(function(resolve, reject) {
    // 模拟异步操作
    setTimeout(function(){
        console.log('3');
        resolve('文件3下载完成');
    }, 1000);
});

download1.then(download2).then(download3);

在download1完成之后会调用download2,download2完成之后会调用download3,实现任务串行,为此,对于那些需要连续执行的异步操作,Promise可以是一种很好的解决办法。

Promise相对于callback,具有更优的代码流,并且具有很好的灵活性。Promise符合自然的事物执行顺序,即先做异步操作,然后再用.then告知下一步该做什么。而在Callback的用法中,先得知道下一步做什么,然后才能将其作为callback函数传入异步操作函数中。

认识Promise

Promise的创建

Promise创建时,会传给promise一个称为excutor执行器的函数。这个excutor我们可以理解为生产者的生产过程函数。这个函数含有两个参数resolvereject,这俩参数也同样是函数,用来传递异步操作的结果

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

有几点值得说一下:

  1. 在Promise对象创建时,excutor会立即执行。
  2. resolvereject是JS引擎自动创建的函数,我们无需自己创建,只需将其作为参数传入就好。

Promise的状态与执行流程

Promise执行流程图如下:

image-20210719192052771.png

创建的promise的内部状态是个对象,初始时为:

{
   state,  //pending
   result,  //undefined
}

当Promise中的异步任务执行完,要么产生value,要么产生error,此时会立即调用resolve(当产生value时)或者调用reject(当产生error)时,内部状态也会随之改变,如下图所示:

image-20210719191048764.png

注意,当excutor里面即使调用了多个resolvereject,其最终还是只执行一个,其他的都被忽略掉。

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

  reject(new Error("…")); // ignored
  setTimeout(() => resolve("…")); // ignored
});

多任务并行

除了串行执行若干异步任务外,Promise还可以并行执行异步任务。

如果一个页面,需要从多个接口下载文件,但假如这些接口之间没有依从性,此时我们可以让多个任务并行以提升效率。

var download1 = new Promise(function(resolve, reject) {
    // 模拟异步操作
    setTimeout(function(){
        console.log('1');
        resolve('文件1下载完成');
    }, 500);
});

var download2 = new Promise(function(resolve, reject) {
    // 模拟异步操作
    setTimeout(function(){
        console.log('2');
        resolve('文件2下载完成');
    }, 800);
});

var download3 = new Promise(function(resolve, reject) {
    // 模拟异步操作
    setTimeout(function(){
        console.log('3');
        resolve('文件3下载完成');
    }, 1000);
});

Promise.all([download1, download2, download3]).then(function (results) {
    console.log(results); // 返回一个数组,包含三个异步任务的执行结果
});

多任务竞争

任务之间是竞争关系,使用Promise也可以很简单的实现。

如果一个页面,需要从多个接口下载文件,但假如只要其中一个任务执行成功获取其结果即可,其它任务便可丢弃。

var download1 = new Promise(function(resolve, reject) {
    // 模拟异步操作
    setTimeout(function(){
        console.log('1');
        resolve('文件1下载完成');
    }, 500);
});

var download2 = new Promise(function(resolve, reject) {
    // 模拟异步操作
    setTimeout(function(){
        console.log('2');
        resolve('文件2下载完成');
    }, 800);
});

var download3 = new Promise(function(resolve, reject) {
    // 模拟异步操作
    setTimeout(function(){
        console.log('3');
        resolve('文件3下载完成');
    }, 1000);
});

Promise.race([download1, download2, download3]).then(function (results) {
    console.log(results); // 返回download1的执行结果
});

由于download1执行较快,所以在then中将获得download1的结果,但是download2与download3任然会继续执行,只是执行的结果将会被丢弃。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容