JavaScript. promise 非同步观念

promise 经常与 Ajax 共同谈论,但这篇文章会以 promise 为主;promise 是一个语法,专门用来处理与优化非同步行为,我们知道 JavaScript 属於同步程序语言,因此它一次只会做一件事,遇到非同步事件的时候,就会等到所有的原始码运行完,才会执行非同步的事件。

这边强调的是执行的顺序,只要是非同步一定都会放在最後才执行,即使 setTimeout() 的时间设定是 0,也同样放到最後。

function getData() {
	setTimeout(() => {
    console.log('... 已取得远端资料');
  }, 0);
}
const component = {
  init() {
    console.log(1); 
    getData(); // 非同步
    console.log(2); 
  }
}
component.init(); 

结果:

1
2
... 已取得远端资料

Promise 结构

promise 是一个建构式函式,函式也属於物件,我们可以附加其他属性方法,promise 可以直接使用 allraceresolvereject 等方法,下面这几种方法皆可以直接 console 查看:

  • promise.all
  • promise.race
  • promise.resolve
  • promise.reject

promise 建构函式 new 出的新物件,也可以在 prototype 内使用其中的原型方法,在这里面也包含了 thencatchfinally,如果要呼叫他们,则必须在新产生的物件下才能呼叫。

透过 new Promise() 的方法建立 a 物件,a 就能使用 promise 的原型方法,范例如下:

const a = new Promise();

a.then(); // promise 回传正确
a.catch(); // promise 回传失败
a.finally(); // 非同步执行完毕(无论是否正确完成,非同步都会在最後被执行完毕)

在 promise 事件里,我们必须给予参数让它回传结果,通常会给予 resolvereject 这两个参数,他们分别代表回传成功及失败,并且仅能回传其中一个。另外,这两个参数也可以自己定义名称,但大部分的开发者会习惯用本来的名字。

new Promise(function(resolve, reject){
	resolve(); // 正确完成的回传
	reject(); // 失败的回传
});

Promise 状态

promise 的执行过程中也包括下面这几种状态:

  • pending / 事件正在运行中,但还没有获得结果。
  • resolved / 事件已经正确执行完毕,并回传 resolve 的结果,resolve 会回传成功。
  • rejected / 事件已经执行完毕,并回传 reject 的结果,reject 会回传错误。

以下范例可以大致说明 promise 的运行过程:

const promiseSetTimeout = (status) => {
  return new Promise((resolve, reject) => {
		// setTimeout 在这里就是一个非同步的状态
    setTimeout(() => {
      if (status) {
        resolve('promiseSetTimeout 成功')
      } else {
        reject('promiseSetTimeout 失败')
      }
    }, 0);
  })
}

pending 之後会有两个状态 Fulfilled 和 Rejected,Fulfilled ( 成功 ) 会使用 resolve 回传结果,并用 then 来做接收,Rejected ( 失败 ) 会使用 reject 回传结果,并用 thencatch 来做接收,但如果前面有 then,则会跳过,直接用 catch 做接收。

另外,我们要如何确定 promise 是否完成?这时候可以依据 resolvereject 是否有被调用,如果没有被调用, promise 的结果会停留在 pending

function promise() {
  return new Promise((resolve, reject) => {});
}

console.dir(promise()); // [[PromiseState]]: "pending"

执行上面程序码,我们可以看到在 promise 函式中有下面两种属性:

  • [[PromiseStatus]]:"pending" 目前的进度状态。
  • [[PromiseValue]]:undefinedresolvereject 回传的值。
promise
	[[Prototype]]: Promise
	[[PromiseState]]: "pending"
	[[PromiseResult]]: undefined

建立 Promise

函式陈述式建立以後,透过 return new Promise 回传并建立一个 promise 物件,在内部执行 promise 函式并加入参数 resolvereject,这个阶段就是常见的 promise 结构,接着等待 resolvereject 回传结果就可以完成整个程序。

这个范例会随机调用 resolve 和 reject:

function promise() {
  return new Promise((resolve, reject) => {
    // 随机取得 0 or 1
    const num = Math.random() > 0.5 ? 1 : 0;

    // 1 则执行 resolve,否则执行 reject
    if (num) { 
      resolve('成功');
    }
    reject('失败')
  });
}

第二个范例也可以看到函式最後的结果是回传 resolve,并且能够回传 resolved 的状态和值。

const promiseSetTimeout = (status) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (status) {
        resolve('promiseSetTimeout 成功')
      } else {
        reject('promiseSetTimeout 失败')
      }
    }, 0);
  })
}
promiseSetTimeout(true)
	.then(function(res){
	console.log(res); // "promiseSetTimeout 成功" 回传成功
})

结果:

Promise {<pending>}
	[[Prototype]]: Promise
	[[PromiseState]]: "fulfilled"
	[[PromiseResult]]: undefined

Promise 链接

promise 在 thencatch 都可以使用链接的方式不断的进行任务,这个范例的 promise 传入 0 会调用 reject,其他数值则是 resolve

function promise(num) {
  return new Promise((resolve, reject) => {
    num ? resolve(`${num}, 成功`) : reject('失败');
  });
}

如果我们希望 promise 可以接着进行下一个任务,可使用 return 进入下一个 then,这边的 return 也有一些特点:

  • 不局限於 promise 函式,任何表达式都可进行回传。
  • promise 函式的 return 会遵循 thencatch 的运作。
  • 如果不是 promise 函式,再下一个 then 则可以取得结果。
promise(1)
  .then(success => {
    console.log(success);
    return promise(2);
  })
  .then(success => {
    console.log(success);
    return promise(0); // 这个阶段会进入 catch
  })
  .then(success => {   // 由於上一个阶段结果是 reject,所以此段不执行
    console.log(success);
    return promise(3);
  })
  .catch(fail => {
    console.log(fail);
  })

Promise 方法

上面有提到几个 promise 的方法,这边回头来讨论一下,在 promise物件下,展开可以看到这些方法。

  • promise.all

以阵列的形式传入数个 promise 函式,并且以同样的顺序回传,且为阵列结果。

Promise.all([promise(1), promise(2), promise(3, 3000)])
  .then(res => {
    console.log(res);
  });
  • promise.race

以阵列的形式传入数个 promise 函式,但在全部执行完毕以後只会回传单一个结果,并且回传的是第一个执行完毕的阵列,以下列范例来说,回传的是 promise(1)

Promise.race([promise(1), promise(2), promise(3, 3000)]).then(res => {
  console.log(res);
});
  • promise.resolve 和 promise.reject

这两个方法其实就是回传 promise 运行完毕的状态;resolve 回传操作成功,reject 回传操作失败,并且通常只会回传其中一个结果。

var result = Promise.resolve('result');
result.then(res => {
  console.log('resolved', res); // 成功部分可以正确接收结果
}, res => {
  console.log('rejected', res); // 失败部分不会取得结果
});

此篇文章感谢卡斯伯老师详细的文章讲解,为了学习和记忆我也尽量用自己的话整理出来,如果有理解错误的地方也请看过的大神们不吝啬告知,或也可以看看卡斯伯老师详细的 promise 全介绍。

参考资料:

JavaScript Promise 全介绍


<<:  [NestJS 带你飞!] DAY31 - 实战演练 (下)

>>:  Golang 学习笔记-- 快速上手/重点整理 - 3 - Array, Slice

Day12 Redis应用实战-String操作

启动Redis Server # 复制安装档内的redis.conf 到自己指定的路径下 cp /h...

Day9# Function 及 Function Receiver

终於来到了第九天,今天要再更深入介绍函式(Function)以及 Function Receiver...

安装与反安装--软件的彼得定律对系统的意义

今天要来讲的东西,主要是来自笔者以前看过很喜欢的文章(https://www.ithome.com....

想办法找找市场区隔吧!

什麽是市场区隔? 最早看到市场区隔这个词的时候 一直不知道算是名词还是动词 後来多看几次文章後慢慢了...

追求JS小姊姊系列 Day9 -- 如果时间能重来,我不想跟工具人聊天(上)

前情提要 上一集让人等到很崩溃的,终於..郑列终於吹嘘完了 阿物件:我跟你说... 我:... (接...