Follow front-end small Ou , read more original technical articles

Contract

  • A contract is a stand-in for a result that does not yet exist, and is a mechanism for asynchronous program execution

related code →

Promises/A+ specification

  • ES6 adds the Promise type, which has become the dominant asynchronous programming mechanism, and all modern browsers support the term contract

Contract basis

  • Promise type is instantiated by the new executor function must be passed in as a parameter
// let p = new Promise() // TypeError: Promise resolver undefined is not a function,必须提供执行器函数作为参数
let p = new Promise(() => {})
setTimeout(console.log, 0, p) // Promise { <pending> }

Contract state machine

  • A contract is a stateful object:

    • Pending pending indicates that has not yet started or is executing . The initial state, which can be settled as a cashed or rejected state, is irreversible after cashing
    • Cashing out fullfilled (or solving resolved ) means successful completion
    • Reject rejected means it did not complete successfully
  • The state of the is 16142f28c9718f private , which encapsulates the isolates external code, cannot by external JS code

Resolution value, rejection reason, and contract use cases

  • The state machine of the contract can provide useful information, suppose it sends an HTTP request to the server:

    • Return the status code in the range of 200-299, which can change the status of the to "cash out". The internal received private JSON string, the default value is undefined
    • Returning a status code that is not in the range of 200-299 will switch the status of the appointment to "rejected". The appointment internal received private Error object (including error messages), the default value is undefined

Control the status of the contract by executing the function

  • The state of the is 16142f28c972b6 private , and can only be completed in executor function internal operation
  • The executor function is responsible for initialization period about asynchronous behavior and control state transition :

    • Control state transition through resolve() and reject()
    • resolve() will switch the status to exchange for , reject() will switch the status to to reject and throw an error
let p1 = new Promise((resolve, reject) => resolve())
setTimeout(console.log, 0, p1) // Promise {<fulfilled>: undefined}

let p2 = new Promise((resolve, reject) => reject())
setTimeout(console.log, 0, p2) // Promise {<rejected>: undefined}
// Uncaught (in promise)
  • The executor function is the expected initialization program, which is executed by synchronously with
new Promise(() => setTimeout(console.log, 0, 'executor'))
setTimeout(console.log, 0, 'promise initialized')
/* 
  'executor',先打印
  'promise initialized',后打印
*/
  • You can add setTimeout postpone the switching state of the actuator function
let p3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 1000)
})
setTimeout(console.log, 0, p3) // Promise { <pending> },打印实例p3时,还不会执行内部回调
  • resolve() and reject() is called, the state transition is irrevocable , continue to modify the state will silent failure
let p4 = new Promise((resolve, reject) => {
  resolve()
  reject() // 静默失败
})
setTimeout(console.log, 0, p4) // Promise {<fulfilled>: undefined}
  • In order to prevent the appointment from being stuck in the pending state, can add the . After setting a certain period of time, the callback of the appointment will be rejected anyway.
let p5 = new Promise((resolve, reject) => {
  setTimeout(reject, 10000) // 10秒后调用reject()
})
setTimeout(console.log, 0, p5) // Promise { <pending> },10秒内,不调用resolve()
setTimeout(console.log, 11000, p5) // Promise {<rejected>: undefined},10秒外,调用reject()
// Uncaught (in promise)

Promise.resolve()

  • Call Promise.resolve() method can instantiate a solved of about
let p6 = new Promise((resolve, reject) => {
  resolve()
})
console.log(p6) // Promise {<fulfilled>: undefined}
let p7 = Promise.resolve()
console.log(p7) // Promise {<fulfilled>: undefined}
  • The first parameter passed to Promise.resolve() is the value of the resolved contract
setTimeout(console.log, 0, Promise.resolve()) // Promise {<fulfilled>: undefined}
setTimeout(console.log, 0, Promise.resolve(3)) // Promise {<fulfilled>: 3}
setTimeout(console.log, 0, Promise.resolve(4, 5, 6)) // Promise {<fulfilled>: 4},只取首个参数
  • Promise.resolve() is a idempotent method, if the incoming parameter is a period of about , its behavior is similar to a empty package
let p8 = Promise.resolve(7)
setTimeout(console.log, 0, Promise.resolve(p8)) // Promise { 7 }
setTimeout(console.log, 0, p8 === Promise.resolve(p8)) // true
setTimeout(console.log, 0, p8 === Promise.resolve(Promise.resolve(p8))) // true
  • The Idempotence will retention passed on about the state
let p9 = new Promise(() => {}) // 待定状态
setTimeout(console.log, 0, p9) // Promise { <pending> }
setTimeout(console.log, 0, Promise.resolve(p9)) // Promise { <pending> }
setTimeout(console.log, 0, Promise.resolve(Promise.resolve(p9))) // Promise { <pending> }
  • This method can wrap any non-value of about (including error object), and converted to resolving about , it may lead to unexpected behavior
let p10 = Promise.resolve(new Error('foo'))
setTimeout(console.log, 0, p10) // Promise {<fulfilled>: Error: foo

Promise.reject()

  • And Promise.resolve() Similarly, Promise.reject() can instantiate a rejected of about and throw an asynchronous error

    • The error cannot be by try/catch , it can only be captured by the rejection handler
let p11 = new Promise((resolve, reject) => {
  reject()
})
console.log(p11) // Promise {<rejected>: undefined}
// Uncaught (in promise)

let p12 = Promise.reject()
console.log(p12) // Promise {<rejected>: undefined}
// Uncaught (in promise)
  • Passed Promise.resolve() of first parameter as an excuse for the period of about, this parameter will passed to subsequent processing procedures reject
let p13 = Promise.reject(3)
setTimeout(console.log, 0, p13) // Promise { <rejected> 3 }
p13.then(null, (err) => setTimeout(console.log, 0, err)) // 3,参数传给后续拒绝处理程序
  • Promise.reject() is not idempotent ( Promise.resolve() ), if the parameter is an appointment object, the appointment in the period becomes the reason for rejection
setTimeout(console.log, 0, Promise.reject(Promise.resolve())) // Promise {<rejected>: Promise}

The duality of synchronous/asynchronous execution

  • Due to the expected asynchronous feature , although it is a synchronous object (which can be used in synchronous execution mode), it is also a medium for asynchronous execution mode

    • synchronous thread cannot capture the rejected date of about , and the error of the rejected date will be processed through the browser asynchronous message queue
    • Once the code execution in an asynchronous mode, the only way to interact is using asynchronous structure , spot about
try {
  throw new Error('foo') // 同步线程抛出错误
} catch (error) {
  console.log(error + '1') // Error: foo1,同步线程捕获错误
}

try {
  Promise.reject('bar') // 同步线程使用期约
} catch (error) {
  console.log(error + '2') // 同步线程捕获不到拒绝的期约
}
// Promise {<rejected>: "bar"},浏览器异步消息队列捕获到拒绝的期约
// Uncaught (in promise) bar

Instance method of contract

  • These methods can access the data returned by asynchronous operations, and deal with the results of successful and failed appointments

Implement the Thenable interface

  • In the asynchronous structure exposed by ECMAScript, any object has a then() method, which is considered to implement the thenable interface
class MyThenable {
  // 实现Thenable接口的最简单的类
  then() {}
}

Promise.prototype.then()

  • Promise.prototype.then() period of about adding handlers , receiving 2 optional parameter handler onResolved and onRejected

    • onResolved be about to enter the period cash execution state
    • onRejected will be executed when the refusal 16142f28c9793c state
function onResolved(id) {
  setTimeout(console.log, 0, id, 'resolved')
}
function onRejected(id) {
  setTimeout(console.log, 0, id, 'rejected')
}
let p14 = new Promise((resolve, reject) => {
  setTimeout(resolve, 3000)
})
let p15 = new Promise((resolve, reject) => {
  setTimeout(reject, 3000)
})

p14.then(
  () => {
    onResolved('p14') // 'p14 resolved'(3秒后)
  },
  () => {
    onRejected('p14')
  }
)
p15.then(
  () => {
    onResolved('p15')
  },
  () => {
    onRejected('p15') // 'p15 rejected'(3秒后)
  }
)
  • Pass then() any non-function type of the parameter will be silently ignored (not recommended), if only onResolved or onRejected , usually in another location parameters for incoming null or undefined
p14.then('gobbeltygook') // 参数不是对象,静默忽略
p14.then(() => onResolved('p14')) // 'p14 resolved'(3秒后),不传onRejected
p15.then(null, () => onRejected('p15')) // 'p15 rejected'(3秒后),不传onResolved
  • Promise.prototype.then() returns a new , which is constructed based on the return value of the onResolved handler ( Promise.resolved()

    • If does not provide this handler , then wrap the value after the resolution of the previous
    • If the handler is provided, but does not show the return statement , the default return value undefined
    • When the processing program provided, and display return values , the wrapper value
    • If a handler is provided, and return of about , the packaging of return of about
    • If a handler is provided, and throws an exception , the package rejected for about
    • If a handler is provided and returns the error value , the error object wrapped in a resolved period (not a rejected period)
let p16 = Promise.resolve('foo')

let result1 = p16.then() // 没有提供处理程序
setTimeout(console.log, 0, result1) // Promise {<fulfilled>: 'foo'},包装上一个期约解决后的值

let result2 = p16.then(() => undefined) // 处理程序没有显示的返回语句
let result3 = p16.then(() => {}) // 处理程序没有显示的返回语句
let result4 = p16.then(() => Promise.resolve()) // 处理程序没有显示的返回语句
setTimeout(console.log, 0, result2) // Promise {<fulfilled>: undefined},包装默认返回值undefined
setTimeout(console.log, 0, result3) // Promise {<fulfilled>: undefined},包装默认返回值undefined
setTimeout(console.log, 0, result4) // Promise {<fulfilled>: undefined},包装默认返回值undefined

let result5 = p16.then(() => 'bar') // 处理程序有显示的返回值
let result6 = p16.then(() => Promise.resolve('bar')) // 处理程序有显示的返回值
setTimeout(console.log, 0, result5) // Promise {<fulfilled>: 'bar'},包装这个值
setTimeout(console.log, 0, result6) // Promise {<fulfilled>: 'bar'},包装这个值

let result7 = p16.then(() => new Promise(() => {})) // 处理程序返回一个待定的期约
let result8 = p16.then(() => Promise.reject('bar')) // 处理程序返回一个拒绝的期约
// Uncaught (in promise) bar
setTimeout(console.log, 0, result7) // Promise {<pending>},包装返回的期约
setTimeout(console.log, 0, result8) // Promise {<rejected>: 'bar'},包装返回的期约

let result9 = p16.then(() => {
  throw 'baz' // 处理程序抛出异常
})
// Uncaught (in promise) baz
setTimeout(console.log, 0, result9) // Promise {<rejected>: 'baz'},包装拒绝的期约

let result10 = p16.then(() => Error('qux')) // 处理程序返回错误值
setTimeout(console.log, 0, result10) // Promise {<fulfilled>: Error: qux},把错误对象包装在一个解决的期约中
  • onResolved as 06142f28c97b2f, when the onRejected handler is used as a parameter, the returned value is also Promise.resolve() , and returns new
let p17 = Promise.reject('foo')

let result11 = p17.then() // 没有提供处理程序
// Uncaught (in promise) foo
setTimeout(console.log, 0, result11) // Promise {<rejected>: 'foo'},包装上一个期约解决后的值

let result12 = p17.then(null, () => undefined) // 处理程序没有显示的返回语句
let result13 = p17.then(null, () => {}) // 处理程序没有显示的返回语句
let result14 = p17.then(null, () => Promise.resolve()) // 处理程序没有显示的返回语句
setTimeout(console.log, 0, result12) // Promise {<fulfilled>: undefined},包装默认返回值undefined
setTimeout(console.log, 0, result13) // Promise {<fulfilled>: undefined},包装默认返回值undefined
setTimeout(console.log, 0, result14) // Promise {<fulfilled>: undefined},包装默认返回值undefined

let result15 = p17.then(null, () => 'bar') // 处理程序有显示的返回值
let result16 = p17.then(null, () => Promise.resolve('bar')) // 处理程序有显示的返回值
setTimeout(console.log, 0, result15) // Promise {<fulfilled>: 'bar'},包装这个值
setTimeout(console.log, 0, result16) // Promise {<fulfilled>: 'bar'},包装这个值

let result17 = p17.then(null, () => new Promise(() => {})) // 处理程序返回一个待定的期约
let result18 = p17.then(null, () => Promise.reject('bar')) // 处理程序返回一个拒绝的期约
// Uncaught (in promise) bar
setTimeout(console.log, 0, result17) // Promise {<pending>},包装返回的期约
setTimeout(console.log, 0, result18) // Promise {<rejected>: 'bar'},包装返回的期约

let result19 = p17.then(null, () => {
  throw 'baz' // 处理程序抛出异常
})
// Uncaught (in promise) baz
setTimeout(console.log, 0, result19) // Promise {<rejected>: 'baz'},包装拒绝的期约

let result20 = p17.then(null, () => Error('qux')) // 处理程序返回错误值
setTimeout(console.log, 0, result20) // Promise {<fulfilled>: Error: qux},把错误对象包装在一个解决的期约中

Promise.prototype.catch()

  • Promise.prototype.catch() for about Add rejection handler , receive 1 optional handler parameter onRejected

    • This method is equivalent to calling Promise.prototypr.then(null, onRejected)
    • The methods also returns new instance of about , behaves Promise.prototype.then() the onRejeted processing program like
let p18 = Promise.reject()
let onRejected2 = function () {
  setTimeout(console.log, 0, 'reject')
}
p18.then(null, onRejected2) // 'reject'
p18.catch(onRejected2) // 'reject',两种添加拒绝处理程序的方式是一样的

Promise.prototype.finally()

  • Promise.prototype.finally() period of about added onFinally handler , receiving . 1 optional parameter handler onFinally

    • Regardless of whether the is resolve rejects the status, the onFinally handler will execute , but it cannot know the status of the contract
    • The method is mainly used add cleanup code
let p19 = Promise.resolve()
let p20 = Promise.reject()
let onFinally = function () {
  setTimeout(console.log, 0, 'Finally')
}
p19.finally(onFinally) // 'Finally'
p20.finally(onFinally) // 'Finally'
  • Promise.prototype.finally() returns a new , the following cases are all packed the transfer of the

    • No handler provided
    • A handler is provided, but there is no return statement shown
    • A handler is provided, and the return value is displayed
    • The handler returns a resolved contract
    • The handler returns an error value
let p21 = Promise.resolve('foo')

let result23 = p21.finally() // 未提供处理程序
let result24 = p21.finally(() => undefined) // 提供了处理程序,但没有显示的返回语句
let result25 = p21.finally(() => {}) // 提供了处理程序,但没有显示的返回语句
let result26 = p21.finally(() => Promise.resolve()) // 提供了处理程序,但没有显示的返回语句
let result27 = p21.finally(() => 'bar') // 提供了处理程序,且有显示的返回值
let result28 = p21.finally(() => Promise.resolve('bar')) // 处理程序返回一个解决的期约
let result29 = p21.finally(() => Error('qux')) // 处理程序返回错误值
setTimeout(console.log, 0, result23) // Promise {<fulfilled>: 'foo'},包装父期约的传递
setTimeout(console.log, 0, result24) // Promise {<fulfilled>: 'foo'},包装父期约的传递
setTimeout(console.log, 0, result25) // Promise {<fulfilled>: 'foo'},包装父期约的传递
setTimeout(console.log, 0, result26) // Promise {<fulfilled>: 'foo'},包装父期约的传递
setTimeout(console.log, 0, result27) // Promise {<fulfilled>: 'foo'},包装父期约的传递
setTimeout(console.log, 0, result28) // Promise {<fulfilled>: 'foo'},包装父期约的传递
setTimeout(console.log, 0, result29) // Promise {<fulfilled>: 'foo'},包装父期约的传递
  • If the onFinally handler returns pending or rejected date or throws an error , then the return value wraps corresponding date (throws error package rejected date)
let result30 = p21.finally(() => new Promise(() => {})) // 处理程序返回一个待定的期约
let result31 = p21.finally(() => Promise.reject()) // 处理程序返回一个拒绝的期约
// Uncaught (in promise) undefined
let result32 = p21.finally(() => {
  throw 'baz' // 处理程序抛出错误
})
// Uncaught (in promise) baz
setTimeout(console.log, 0, result30) // Promise {<pending>},返回相应的期约
setTimeout(console.log, 0, result31) // Promise {<rejected>: undefined},返回相应的期约
setTimeout(console.log, 0, result32) // Promise {<rejected>: 'baz'},返回相应的期约
  • onFinally processing program returns to pending contract is resolved, , the new contract instance still transmits the initial contract
let p22 = Promise.resolve('foo')
let p23 = p22.finally(
  () => new Promise((resolve, reject) => setTimeout(() => resolve('bar'), 100)) // 处理程序返回一个待定的期约(100毫秒后解决)
)
setTimeout(console.log, 0, p23) // Promise {<pending>},返回相应的期约
setTimeout(() => setTimeout(console.log, 0, p23), 200) // Promise {<fulfilled>: "foo"},(200毫秒后)待定的期约已解决

Non-reentry contract method

  • When the appointment enters the state (resolved/rejected) processing program related to this state will not immediately execute synchronization code after the processing program will be executed . This feature is called non-reentrant
let p24 = Promise.resolve() // 解决的期约,已落定
p24.then(() => console.log('onResolved handler')) // 与期约状态相关的onResolved处理程序
console.log('then() returns') // 处理程序之后的同步代码
/* 
  'then() returns',处理程序之后的同步代码先执行
  'onResolved handler'
*/
  • Even in a period of about after the handler only changes state (resolved / rejected) , handlers still showed non-reentrant properties
let synchronousResolve // 全局方法:期约状态状态
let p25 = new Promise((resolve) => {
  synchronousResolve = function () {
    console.log('1: invoking resolve()')
    resolve() // 期约状态改变
    console.log('2: resolve() returns')
  }
})
p25.then(() => console.log('4: then() handler executes')) // 与期约状态相关的onResolved处理程序
synchronousResolve() // 处理程序之后的同步代码:期约状态改变
console.log('3: synchronousResolve() returns') // 处理程序之后的同步代码
/* 
  '1: invoking resolve()'
  '2: resolve() returns'
  '3: synchronousResolve() returns'
  '4: then() handler executes'
*/
  • Non-reentrant features are applicable to onResolved , onRejected , catch() , finally() processing procedures
let p26 = Promise.resolve()
p26.then(() => console.log('p26.then() onResolved'))
console.log('p26.then() returns')

let p27 = Promise.reject()
p27.then(null, () => console.log('p27.then() onRejected'))
console.log('p27.then() returns')

let p28 = Promise.reject()
p28.catch(() => console.log('p28.catch() onRejected'))
console.log('p28.catch() returns')

let p29 = Promise.resolve()
p26.finally(() => console.log('p29.finally() onFinally'))
console.log('p29.finally() returns')
/* 
  'p26.then() returns'
  'p27.then() returns'
  'p28.catch() returns'
  'p29.finally() returns'
  'p26.then() onResolved'
  'p27.then() onRejected'
  'p28.catch() onRejected'
  'p29.finally() onFinally'
*/

The order of execution of neighboring handlers

  • If added of about plurality of handlers , when a state change about the current period, a Handler order of addition sequentially performed

    • The processing procedures added by then() , catch() , finally()
let p30 = Promise.resolve()
let p31 = Promise.reject()

p30.then(() => setTimeout(console.log, 0, 1))
p30.then(() => setTimeout(console.log, 0, 2))

p31.then(null, () => setTimeout(console.log, 0, 3))
p31.then(null, () => setTimeout(console.log, 0, 4))

p31.catch(() => setTimeout(console.log, 0, 5))
p31.catch(() => setTimeout(console.log, 0, 6))

p30.finally(() => setTimeout(console.log, 0, 7))
p30.finally(() => setTimeout(console.log, 0, 8))
/* 
  1
  2
  3
  4
  5
  6
  7
  8
*/

Pass the resolution value and the reason for rejection

  • After the settled (resolved/rejected) , the processing program will be provided with settlement value (cash out) or reason for rejection (reject)

    • In execution function , the value of resolve and refusal are as resolve() and reject() of first parameter , passed onResolved and onRejected handler (as only parameter )
    • When Promise.resolve() and Promise.reject() are called, the received resolution value and rejection reason are also passed back to the handler (as its only parameter )
let p32 = new Promise((resolve, reject) => resolve('foo')) // 执行函数中
p32.then((value) => console.log(value)) // 'foo'
let p33 = new Promise((resolve, reject) => reject('bar')) // 执行函数中
p33.catch((reason) => console.log(reason)) // 'bar'

let p34 = Promise.resolve('foo') // Promise.resolve()中
p34.then((value) => console.log(value)) // 'foo'
let p35 = Promise.reject('bar') // Promise.reject()中
p35.catch((reason) => console.log(reason)) // 'bar'

Rejection of contract and rejection error handling

  • In the period of about perform functions or handler throw an error will result in a denial, error object become refusal
let p36 = new Promise((resolve, reject) => reject(Error('foo'))) // 在执行函数中抛出错误
let p37 = new Promise((resolve, reject) => {
  throw Error('foo') // 在执行函数中抛出错误
})
let p38 = Promise.resolve().then(() => {
  throw Error('foo') // 在处理程序中抛出错误
})
let p39 = Promise.reject(Error('foo')) // 在拒绝的期约中抛出错误
setTimeout(console.log, 0, p36) // Promise {<rejected>: Error: foo
setTimeout(console.log, 0, p37) // Promise {<rejected>: Error: foo
setTimeout(console.log, 0, p38) // Promise {<rejected>: Error: foo
setTimeout(console.log, 0, p39) // Promise {<rejected>: Error: foo
  • Can any reason refuse, including undefined , but the best uniform use of error object , error object allows the browser to capture them stack trace information
  • If the above-mentioned refusal of an appointment, 4 uncaught errors will be thrown in the browser:

    • Promise.resolve().then() only appeared last, because add handler at runtime (that is, create another new appointment before catching)
/* 上述拒绝期约会抛出4个未捕获错误:栈追踪信息
  Uncaught (in promise) Error: foo
  at <anonymous>:1:51
  at new Promise (<anonymous>)
  at <anonymous>:1:11
  (anonymous)    @    VM1402:1
  (anonymous)    @    VM1402:1

  Uncaught (in promise) Error: foo
  at <anonymous>:3:9
  at new Promise (<anonymous>)
  at <anonymous>:2:11
  (anonymous)    @    VM1402:3
  (anonymous)    @    VM1402:2

  Uncaught (in promise) Error: foo
  at <anonymous>:8:26
  (anonymous)    @    VM1402:8

  Uncaught (in promise) Error: foo
  at <anonymous>:6:9
  (anonymous)    @    VM1402:6
  Promise.then (async)        
  (anonymous)    @    VM1402:5
*/
  • asynchronous error is different from synchronization:

    • When the synchronization code throw() keyword, it will stop executing any subsequent commands
    • , the synchronous instruction will not be blocked, and the 16142f28c98ba3 error can only be captured onRejected handler
throw Error('foo') // 同步代码抛出错误(try/catch中能捕获)
console.log('bar') // 后续任何指令不再执行
// Uncaught Error: foo,浏览器消息队列

Promise.reject(Error('foo')) // 期约中抛出错误(try/catch中捕获不到)
console.log('bar') // 'bar',同步指令继续执行
// Uncaught (in promise) Error: foo,浏览器消息队列

Promise.reject(Error('foo')).catch((e) => {
  console.log(e) // 'Error: foo',在期约中捕获
})
  • execute function error, resolved or rejected, it can still be captured try/catch
let p40 = new Promise((resolve, reject) => {
  try {
    throw Error('foo')
  } catch (error) {}
  resolve('bar')
})
setTimeout(console.log, 0, p40) // Promise {<fulfilled>: 'bar'}
  • then() and catch() the onRejected handler semantically try/catch same (which will catch the error isolation, does not affect the normal logic), thus onReject handler after capture asynchronous error returns a solution of about
console.log('begin synchronous execution')
try {
  throw Error('foo') // 抛出同步错误
} catch (error) {
  console.log('caught error', error) // 捕获同步错误
}
console.log('continue synchronous execution')
/*
  'begin synchronous execution'
  'caught error Error: foo'
  'continue synchronous execution'
*/

new Promise((resolve, reject) => {
  console.log('begin synchronous execution')
  reject(Error('bar')) // 抛出异步错误
})
  .catch((e) => {
    console.log('caught error', e) // 捕获异步错误
  })
  .then(() => {
    console.log('continue synchronous execution')
  })
/*

Contract linkage and contract synthesis

  • Multiple period appointments together can form a powerful code logic: period contract linkage (splicing) and period contract synthesis (combination)

Contract chain

  • The instance methods of each appointment ( then() , catch() , finally() ) return a new instance of the appointment, and multiple appointments can be called in form 16142f28c98e32,
let p41 = new Promise((resolve, reject) => {
  console.log('first')
  resolve()
})
p41
  .then(() => console.log('second'))
  .then(() => console.log('third'))
  .then(() => console.log('fourth'))
/* 
  'first'
  'second'
  'third'
  'fourth'
*/
  • If you want serialize the asynchronous task , you need to make return to the instance ,
let p42 = new Promise((resolve, reject) => {
  console.log('p42 first')
  setTimeout(resolve, 1000)
})
p42
  .then(
    () =>
      // 执行器返回期约实例
      new Promise((resolve, reject) => {
        console.log('p42 second')
        setTimeout(resolve, 1000)
      })
  )
  .then(
    () =>
      // 执行器返回期约实例
      new Promise((resolve, reject) => {
        console.log('p42 third')
        setTimeout(resolve, 1000)
      })
  )
  .then(
    () =>
      // 执行器返回期约实例
      new Promise((resolve, reject) => {
        console.log('p42 fourth')
        setTimeout(resolve, 1000)
      })
  )
/* 
  'p42 first'(1秒后)
  'p42 second'(2秒后)
  'p42 third'(3秒后)
  'p42 fourth'(4秒后)
*/
  • The same code encapsulated into a factory function
function delayedResolve(str) {
  return new Promise((resolve, reject) => {
    console.log(str)
    setTimeout(resolve, 1000)
  })
}
delayedResolve('p42 first')
  .then(() => delayedResolve('p42 second'))
  .then(() => delayedResolve('p42 third'))
  .then(() => delayedResolve('p42 fourth'))
/* 
  'p42 first'(1秒后)
  'p42 second'(2秒后)
  'p42 third'(3秒后)
  'p42 fourth'(4秒后)
*/
  • chain can effectively solve the 16142f28c98fc2 callback hell problem with 16142f28c98fc0. The above code is as follows if the
function delayedNotPromise(str, callback = null) {
  setTimeout(() => {
    console.log(str)
    callback && callback()
  }, 1000)
}
delayedNotPromise('p42 first', () => {
  delayedNotPromise('p42 second', () => {
    delayedNotPromise('p42 third', () => {
      delayedNotPromise('p42 fourth', () => {})
    })
  })
})
/* 
  'p42 first'(1秒后)
  'p42 second'(2秒后)
  'p42 third'(3秒后)
  'p42 fourth'(4秒后)
*/
  • then() , catch() finally() all return to the new contract instance, and the contract can be chained arbitrarily
let p43 = new Promise((resolve, reject) => {
  console.log('p43')
  reject()
})
p43
  .catch(() => console.log('p43 catch'))
  .then(() => console.log('p43 then'))
  .finally(() => console.log('p43 finally'))
/* 
  'p43'
  'p43 catch'
  'p43 then'
  'p43 finally'
*/

Contract chart

  • An appointment can have any number of processing procedures, and an appointment chain can construct directed acyclic graph
- A
  - B
    - D
    - E
  - C
    - F
    - G
let A = new Promise((resolve, reject) => {
  console.log('A')
  resolve()
})
let B = A.then(() => console.log('B'))
let C = A.then(() => console.log('C'))
B.then(() => console.log('D'))
B.then(() => console.log('E'))
C.then(() => console.log('F'))
C.then(() => console.log('F'))
/* 
  'A'
  'B'
  'C'
  'D'
  'E'
  'F'
*/

Promise.all() and Promise.race()

  • Promise.all() receiving a iterable (will pass), returns a new period of about , its date of creation of the after a set period of approximately solve all to resolve

    • The elements in the iterable object are converted to a contract Promise.resolve()
    • The empty iteration object is equivalent to Promise.resolve()
Promise.all([Promise.resolve(), Promise.resolve()]) // 接收1组可迭代对象
Promise.all([3, 4]) // 可迭代对象中的元素通过Promise.resolve()转换为期约
Promise.all([]) // 空迭代对象等价于Promise.resolve()
Promise.all() // TypeError: undefined is not iterable (cannot read property Symbol(Symbol.iterator)),参数必填
  • Promise.all() synthetic period of about only in after each stage contains about are resolved only solution
let p44 = Promise.all([
  Promise.resolve(),
  new Promise((resolve, reject) => setTimeout(resolve, 1000)),
])
p44.then(() => setTimeout(console.log, 0, 'all() resolved!')) // 'all() resolved!'(1秒后,非0秒,需等包含的期约先解决)
  • If there is 1 included futures contract pending, then the pending contract will be synthesized, and the same is true for rejection (rejection takes precedence over pending)
let p45 = Promise.all([new Promise(() => {}), Promise.resolve()]) // 包含的期约有待定的
setTimeout(console.log, 0, p45) // Promise {<pending>},合成待定的期约
let p46 = Promise.all([new Promise(() => {}), Promise.reject()]) // 包含的期约有拒绝的(也有待定的)
// Uncaught (in promise) undefined
setTimeout(console.log, 0, p46) // Promise {<rejected>: undefined},合成拒绝的期约
  • If all the included contracts are successfully resolved, the resolved contracts are synthesized, and the resolved value is an array of all the resolved values (in the order of the iterator) that contains the futures.
let p47 = Promise.all([
  Promise.resolve(1),
  Promise.resolve(),
  Promise.resolve(3),
]) // 包含的所有期约都解决
setTimeout(console.log, 0, p47) // Promise {<fulfilled>: [1, undefined, 3]}
  • If Rejecting about, first rejected on the grounds of his appointment as synthesis phase about the reasons for the refusal (follow-up will not affect the synthesis of reason about the grounds for refusal), but not affect follow-up period of approximately Reject operation
let p48 = Promise.all([
  Promise.reject(3), // 第一个拒绝的期约,拒绝理由为3
  new Promise((resolve, reject) => setTimeout(reject, 1000, 4)), // 第二个拒绝的期约,拒绝理由为4
])
// Uncaught (in promise) 3
setTimeout(console.log, 0, p48) // Promise {<rejected>: 3},第一个拒绝理由作为合成期约的拒绝理由
p48.catch((reason) => setTimeout(console.log, 2000, reason)) // 3,第一个拒绝理由作为合成期约的拒绝理由,但浏览器不会显示未处理的错误(Uncaught (in promise) 3)
  • Promise.race() is Promise.all() . It receives a iterable object (required), and package set is settled first (resolved or rejected) period contract resolution value or rejection reason and returns new period about

    • The elements in the iterable object are converted to a contract Promise.resolve()
    • The empty iteration object is equivalent to Promise.resolve()
    • The order of iteration determines the order of settlement
    • first refusal date will use its own reason as the reason for synthetic futures , but not affect the operation of the refusal of the subsequent futures contract.
Promise.race([Promise.resolve(), Promise.resolve()]) // 接收1组可迭代对象
Promise.race([3, 4]) // 可迭代对象中的元素通过Promise.resolve()转换为期约
Promise.race([]) // 空迭代对象等价于Promise.resolve()
// Promise.all() // TypeError: undefined is not iterable (cannot read property Symbol(Symbol.iterator)),参数必填

let p49 = Promise.race([
  Promise.resolve(3),
  new Promise((resolve, reject) => setTimeout(reject, 1000)),
])
setTimeout(console.log, 0, p49) // Promise {<fulfilled>: 3},解决先发生,超时后的拒绝被忽略

let p50 = Promise.race([
  Promise.reject(4),
  new Promise((resolve, reject) => setTimeout(resolve, 1000)),
])
// Uncaught (in promise) 4
setTimeout(console.log, 0, p50) // Promise {<rejected>: 4},拒绝先发生,超时后的解决被忽略

let p51 = Promise.race([
  Promise.resolve(1),
  Promise.resolve(),
  Promise.resolve(3),
])
setTimeout(console.log, 0, p51) // Promise {<fulfilled>: 1},迭代顺序决定落定顺序

let p52 = Promise.race([
  Promise.reject(3), // 第一个拒绝的期约,拒绝理由为3
  new Promise((resolve, reject) => setTimeout(reject, 1000, 4)), // 第二个拒绝的期约,拒绝理由为4
])
// Uncaught (in promise) 3
setTimeout(console.log, 0, p52) // Promise {<rejected>: 3},第一个拒绝理由作为合成期约的拒绝理由
p52.catch((reason) => setTimeout(console.log, 2000, reason)) // 3,第一个拒绝理由作为合成期约的拒绝理由,但浏览器不会显示未处理的错误(Uncaught (in promise) 3)

Serial contract synthesis

  • Multiple functions can be combined into one function
function addTwo(x) {
  return x + 2
}
function addThree(x) {
  return x + 3
}
function addFive(x) {
  return x + 5
}

function addTen(x) {
  return addFive(addThree(addTwo(x)))
}
console.log(addTen(7)) // 17
  • Similar to function synthesis, the value of asynchronously by 16142f28c99a7a and passed to the processing program . The return value of from the previous period can be in subsequent periods to concatenate the value of 16142f28c99a85.
function addTen(x) {
  return Promise.resolve(x).then(addTwo).then(addThree).then(addFive)
}
setTimeout(console.log, 0, addTen(8)) // Promise {<fulfilled>: 18}
addTen(8).then((result) => console.log(result)) // 18
  • You can use Array.prototype.reduce() abbreviate the above-mentioned contract series
function addTen(x) {
  return [addTwo, addThree, addFive].reduce((pre, cur) => {
    return pre.then(cur)
  }, Promise.resolve(x)) // 归并起点值(归并函数第1个参数)为Promise.resolve(x),第2个参数为数组第1项addTwo
}
setTimeout(console.log, 0, addTen(9)) // Promise {<fulfilled>: 19}
addTen(9).then((result) => console.log(result)) // 19
  • The final package into a general method
function compose(...fns) {
  return (x) =>
    fns.reduce((pre, cur) => {
      return pre.then(cur)
    }, Promise.resolve(x))
}
addTen = compose(addTwo, addThree, addFive)
addTen(10).then((result) => console.log(result)) // 20

Contract extension

  • has its shortcomings. Two features not covered by ECMAScript 16142f28c99b8d, and Progress Tracking have been implemented in many third-party futures libraries

Cancellation

  • package can be provided to realize the cancellation period of about
const startButton = document.querySelector('#start') // 开始按钮
const cancelButton = document.querySelector('#cancel') // 结束按钮
let cancelBtnHasClickEvent = false // 结束按钮是否已添加点击事件
/* 
  书中案例每次点击“开始”按钮,都会重新实例化CancelToken实例,给cancelToken追加一个点击事件,打印的'delay cancelled'会随之越来越多
  这里追加一个全局变量cancelBtnHasClickEvent,确保只在首次点击“开始”按钮时,给cancelToken只追加一次点击事件
*/

// CancelToken类,包装一个期约,把解决方法暴露给cancelFn参数
class CancelToken {
  constructor(cancelFn) {
    this.promise = new Promise((resolve, reject) => {
      cancelFn(() => {
        setTimeout(console.log, 0, 'delay cancelled') // 取消计时
        resolve() // 期约解决
      })
    })
  }
}

// 点击事件:开始计时、实例化新的CancelToken实例
function cancellabelDelayedResolve(delay) {
  setTimeout(console.log, 0, 'set delay') // 开始计时
  return new Promise((resolve, reject) => {
    const id = setTimeout(() => {
      setTimeout(console.log, 0, 'delay resolve') // 经延时后触发
      resolve()
    }, delay)
    // 实例化新的CancelToken实例
    const cancelToken = new CancelToken((cancelCallback) => {
      cancelBtnHasClickEvent === false &&
        cancelButton.addEventListener('click', cancelCallback) // 结束按钮添加点击事件
      cancelBtnHasClickEvent = true // 结束按钮已添加点击事件
    })
    cancelToken.promise.then(() => clearTimeout(id)) // 触发令牌实例中的期约解决
  })
}

startButton.addEventListener('click', () => cancellabelDelayedResolve(1000)) // 开始按钮添加点击事件

complete file →

Notification of contract progress

  • ES6 does not support monitoring the execution progress of the contract, it can be achieved through extension
// 子类TrackablePromise,继承父类Promise
class TrackablePromise extends Promise {
  // 子类构造函数,接收1个参数(executor函数)
  constructor(executor) {
    const notifyHandlers = []
    // super()调用父类构造函数constructor(),传入参数(执行器函数)
    super((resolve, reject) => {
      // 执行executor()函数,参数为传给TrackablePromise子类的参数,返回执行的结果
      return executor(resolve, reject, (status) => {
        console.log(status)
        /* 
          '80% remaining'(约1秒后)
          '60% remaining'(约2秒后)
          'remaining'(约3秒后)
          'remaining'(约4秒后)
        */
        notifyHandlers.map((handler) => {
          return handler(status)
        })
      })
    })
    this.notifyHandlers = notifyHandlers
  }
  // 添加notify方法,接收1个参数(notifyhandler函数)
  notify(notifyHandler) {
    this.notifyHandlers.push(notifyHandler)
    return this
  }
}

// 创建子类实例,传入参数(executor函数)
let p53 = new TrackablePromise((resolve, reject, notify) => {
  function countdown(x) {
    if (x > 0) {
      notify(`${20 * x}% remaining`)
      setTimeout(() => countdown(x - 1), 1000)
    } else {
      resolve()
    }
  }
  countdown(5)
})
console.log(p53) // Promise {<pending>, notifyHandlers: Array(0)},TrackablePromise实例(子类期约)

p53.notify((x) => setTimeout(console.log, 0, 'progress:', x)) // 调用期约实例的notify()方法,传入参数(notifyhandler函数)
p53.then(() => setTimeout(console.log, 0, 'completed')) // 调用期约实例的then()方法,传入参数(onResolved处理程序)
/* 
  'progress: 80% remaining'(约1秒后)
  'progress: 60% remaining'(约2秒后)
  'progress: 40% remaining'(约3秒后)
  'progress: 20% remaining'(约4秒后)
  'completed'(约5秒后)
*/

p53
  .notify((x) => setTimeout(console.log, 0, 'a:', x))
  .notify((x) => setTimeout(console.log, 0, 'b:', x)) // notice()返回期约,连缀调用
p53.then(() => setTimeout(console.log, 0, 'completed'))
/* 
  'a: 80% remaining'(约1秒后)
  'b: 80% remaining'(约1秒后)
  'a: 60% remaining'(约2秒后)
  'b: 60% remaining'(约2秒后)
  'a: 40% remaining'(约3秒后)
  'b: 40% remaining'(约3秒后)
  'a: 20% remaining'(约4秒后)
  'b: 20% remaining'(约4秒后)
  'completed'(约5秒后)
*/

Summary & Questions

  • What is the Promise type? How to create? What do the different states mean?
  • What is the role of the executor function? How to postpone its switching state? How to prevent the contract from being stuck in the pending state?
  • How to instantiate a resolved contract? What is its value? What if the incoming parameters are also the result of the contract?
  • How to instantiate a rejected contract? What is the reason for its refusal? What if the incoming parameters are also the result of the contract?
  • What are the meanings of Promise.prototype.then(), Promise.prototype.catch(), and Promise.prototype.finally()? Which parameters are received respectively? According to the different parameters, what are the return values?
  • How to understand the "non-reentry" of the contract? Which processing procedures does it apply to?
  • If multiple processing procedures are added to the same contract, in what order will the processing procedures be executed when their status changes? How to pass the settlement value of the contract and the reason for rejection to the processing program?
  • How to pass the resolution value and the reason for rejection? How to catch the error object when an error is thrown? Why is it better to use the wrong object as the reason for rejection?
  • Write a piece of code to capture the [synchronous and asynchronous] errors in [try/catch and contract] separately, without affecting the normal other (follow-up) code logic
  • Write a piece of code to complete the serialization of multiple asynchronous tasks: ① Do not use a contract to form a "callback hell" ② Use a chain to solve this problem with a contract
  • What is the meaning of Promise.all() and Promise.race()? What kind of contract will it return under different circumstances?
  • Write a piece of code, concatenate multiple contracts, then use reduce() to simplify the code, and finally encapsulate it into a general method
  • Write a piece of code to achieve the following functions: the page has 2 buttons [Start] and [End], click [Start] to instantiate a new appointment and print "Start", the appointment will be honored after 1 second and print "Success", click [End] Cancel the contract and print "End"

小讴
217 声望16 粉丝